zoukankan      html  css  js  c++  java
  • SSM框架之Spring(3)IOC及依赖注入(基于注解的实现)

    Spring(3)IOC及依赖注入(基于注解的实现)

    学习基于注解的 IoC 配置,大家脑海里首先得有一个认知,即注解配置和 xml 配置要实现的功能都是一样
    的,都是要降低程序间的耦合。只是配置的形式不一样。

    关于实际的开发中到底使用xml还是注解,每家公司有着不同的使用习惯。所以这两种配置方式我们都需要掌
    握。

    环境配置

    bean.xml的配置

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--把对象创建交给spring管理-->
    
        <context:component-scan base-package="domain"></context:component-scan>
    </beans>
    

    对具体类的配置

    package domain.service.impl;
    
    import domain.service.IAccountService;
    import domain.dao.IAccountDao;
    import org.springframework.context.annotation.Scope;
    import org.springframework.stereotype.Service;
    
    import javax.annotation.PostConstruct;
    import javax.annotation.PreDestroy;
    import javax.annotation.Resource;
    
    @Service("accountService")
    @Scope
    public class AccountServiceImpl implements IAccountService {
    //    @Autowired
    //    @Qualifier("accountDao1")
        @Resource(name = "accountDao1")
        private IAccountDao accountDao = null;
    
    
        public AccountServiceImpl() {
            System.out.println("对象创建成功");
    //        System.out.println("a = "+ a);
        }
    
        public void saveAccount() {
            accountDao.saveAccount();
        }
    
        @PostConstruct
        public void init() {
            System.out.println("初始化。。");
        }
    
        @PreDestroy
        public void destory() {
            System.out.println("销毁方法。。");
        }
    }
    

    1、常用注解

    <bean id="accountService" class="domain.service.impl.AccountServiceImpl" scope="singleton" init-method="" destroy-method="">
        <property name="" value="" ref=""></property>
     </bean>
    

    1.1、用于创建对象的

    作用等同于xml配置中的bean标签

    <bean id="" class=""></bean>
    

    1.1.1、Component

    作用:把被注解的类创建对象后放入spring容器中
    属性:
    value:指定当前的bean的id,如果不写那么他的默认值是被注解的类的类名,且首字母小写

    @Service("accountDao1")
    public class AccountDaoImpl implements IAccountDao {
        public void saveAccount() {
            System.out.println("保存账户成功!!!11111111111");
        }
    }
    

    1.1.2、Controller,Service,Repository

    以上三个注解的作用和属性与Component完全相同,
    它们是spring提供的三层架构的注解,使三层对象更加清晰

    @Controller :一般用于表现层的注解。
    @Service :一般用于业务层的注解。
    @Repository :一般用于持久层的注解。

    细节:如果注解中有且只有一个属性 要赋值时是 ,且名称是 value ,value 在赋值是可以不写。

    1.2、用于注入数据

    作用等同于xml中的bean标签下的properties标签

    <bean id="" class="">
        <property name="" value=""/ref=""></property>
    </bean>
    

    1.2.1、@Autowired

    作用:自动按照类型注入,只要有唯一的一个bean对象与要注入的类型匹配,就可以注入成功
    如果IOC容器中没有与要注入变量同类型的bean,则报错
    如果IOC容器中有多个与之类型匹配,在不使用其他注解的情况下,依据要注入变量的名称和bean对象的id进行比较,如果变量名与id相同就可以注入,如果无匹配id则报错

    出现位置:
    变量和方法上都可以出现

    细节:使用注解注入时,set方法非必须

    @Autowired
    private IAccountDao accountDao = null;
    

    1.2.2、Qualifier:

    作用:
    在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。它在给字段注入时不能独立使用,必须和
    @Autowire 一起使用;但是给方法参数注入时,可以独立使用。

    属性:
    value:指定 bean 的 id。

    @Autowired
    @Qualifier("accountDao1")
    private IAccountDao accountDao = null;
    

    1.2.3、@Resource

    作用:
    直接按照 Bean 的 id 注入。它也只能注入其他 bean 类型。

    属性:
    name:指定 bean 的 id。

    @Resource(name = "accountDao1")
    private IAccountDao accountDao = null;
    

    1.2.4、@Value

    作用:
    注入基本数据类型和 String 类型数据的

    属性:
    value:用于指定数据的值。它可以使用spring中的spEl(spring中的EL表达式)

    spEL写法:${表达式}

    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    

    1.3、用于改变作用范围的

    作用等同于xml配置中bean标签的scope属性

    @Service("accountDao1")
    @Scope("prototype")
    public class AccountDaoImpl implements IAccountDao {
        public void saveAccount() {
            System.out.println("保存账户成功!!!11111111111");
        }
    }
    

    1.3.1、 @Scope

    作用:
    指定 bean 的作用范围。
    属性:
    value:指定范围的值。
    取值:singleton prototype request session globalsession

    1.4、改变作用范围

    作用等同于xml配置中的init-method和destory-method属性

    @PostConstruct
    public void init() {
        System.out.println("初始化。。");
    }
    
    @PreDestroy
    public void destory() {
        System.out.println("销毁方法。。");
    }
    

    1.4.1、@PostConstruct

    作用:
    用于指定初始化方法。== init-method

    1.4.2、@PreDestroy

    作用:
    用于指定销毁方法。==destory-method

    1.5、关于 Spring 注解和 XML

    注解的优势:
    配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。适用于自己定义的类
    XML 的优势:
    修改时,不用改源码。不涉及重新编译和部署。适用于通过jar导入的类

    2、spring 的纯注解配置

    写到此处,基于注解的 IoC 配置已经完成,但是大家都发现了一个问题:我们依然离不开 spring 的 xml 配
    置文件,那么能不能不写这个 bean.xml,所有配置都用注解来实现呢?

    2.1、问题

    之所以我们现在离不开 xml 配置文件,是因为我们有一句很关键的配置:

    <!-- 告知spring框架在,读取配置文件,创建容器时,扫描注解,依据注解创建对象,并存入容器中 -->
    <context:component-scan base-package="com.itheima"></context:component-scan>
    

    如果他要也能用注解配置,那么我们就离脱离 xml 文件又进了一步。
    另外,数据源和 JdbcTemplate 的配置也需要靠注解来实现。

    <!-- 配置 dbAssit -->
    <bean id="dbAssit" class="com.itheima.dbassit.DBAssit">
    <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
    <property name="jdbcUrl" value="jdbc:mysql:///spring_day02"></property>
    <property name="user" value="root"></property>
    <property name="password" value="1234"></property>
    </bean>
    

    2.2、新注解说明

    2.2.1、@Configuration

    作用:
    用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。获取容器时需要使用
    AnnotationApplicationContext(有@Configuration 注解的类.class)。

    属性:
    value:用于指定配置类的字节码

    细节:当配置类作为AnnotationConfigApplicationContext对象创建容器的参数该注解可以省略

    @Configuration
    public class SpringCinfiguration {
    
    }
    

    注意:
    我们已经把配置文件用类来代替了,但是如何配置创建容器时要扫描的包呢?
    请看下一个注解。

    2.2.2、@ComponentScan

    作用:通过注解指定spring容器在创建时要扫描的包
    属性:value:和basePackages作用相同,都是指定要创建容器要扫描的包
    该注解作用相当于在xml中配置的:

         <context:component-scan base-package="wf"></context:component-scan
    
    @Configuration
    @ComponentScan(basePackages = {"wf","config"})//wf,config是包名
    public class SpringCinfiguration {
    
    }
    

    注意:
    我们已经配置好了要扫描的包,但是数据源和 JdbcTemplate 对象如何从配置文件中移除呢?
    请看下一个注解。

    2.2.3、@Bean

    作用:把当前方法的返回值作为bean对象存入spring的ioc容器中

    ​ 属性:
    ​ name:用于指定bean对象的id,当不写时默认是当前方法名

    ​ 细节:
    ​ 当我们使用注解配置方法时,如果方法有参数,sprig框架就会去容器中查找看是否由可用bean对象
    查找方式和Autowired注解的作用一样

    @Bean(name = "runner")
    public QueryRunner creatQueryRunner(@Qualifier("ds1") DataSource dataSource){
        return new QueryRunner(dataSource);
    }
    

    注意:
    我们已经把数据源和 DBAssit 从配置文件中移除了,此时可以删除 bean.xml 了。
    但是由于没有了配置文件,创建数据源的配置又都写死在类中了。如何把它们配置出来呢?
    请看下一个注解。

    2.2.4、@PropertySource

    jdbc.properties

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql:///spring
    jdbc.username=root
    jdbc.password=123456
    

    作用:

    用于加载.properties 文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到
    properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。

    属性:
    value[]:用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath:

    @Configuration
    @PropertySource("classpath:jdbc.properties")
    public class JdbcConfig {
        @Value("${jdbc.driver}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    }
    

    注意:
    此时我们已经有了两个配置类,但是他们还没有关系。如何建立他们的关系呢?
    请看下一个注解。

    2.2.5、@Import

    作用:用于导入其他的配置类,在引入其他配置类时,可以不用再写@Configuration 注解。当然,写上也没问
    题。

    属性:
    value[]:用于指定其他配置类的字节码。

    @Configuration
    @ComponentScan(basePackages = {"wf","config"})
    @Import({JdbcConfig.class})
    public class SpringCinfiguration {
    
    }
    

    注意:
    我们已经把要配置的都配置好了,但是新的问题产生了,由于没有配置文件了,如何获取容器呢?
    请看下一小节。

    2.2.6、通过注解获取容器:

    AnnotationConfigApplicationContext ca = new AnnotationConfigApplicationContext(SpringCinfiguration.class);
    

    3、Spring 整合 Junit

    3.1、问题

    在测试类中,每个测试方法都有以下两行代码:
    ApplicationContext ac = new ClassPathXmlApplicationContext(“bean.xml”);
    IAccountService as = ac.getBean(“accountService”,IAccountService.class);
    这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。

    3.2、解决思路分析

    针对上述问题,我们需要的是程序能自动帮我们创建容器。一旦程序能自动为我们创建 spring 容器,我们就无须手动创建了,问题也就解决了。

    我们都知道,junit 单元测试的原理(在 web 阶段课程中讲过),但显然,junit 是无法实现的,因为它自己都无法知晓我们是否使用了 spring 框架,更不用说帮我们创建 spring 容器了。不过好在,junit 给我们暴露了一个注解,可以让我们替换掉它的运行器。

    这时,我们需要依靠 spring 框架,因为它提供了一个运行器,可以读取配置文件(或注解)来创建容器。我们只需要告诉它配置文件在哪就行了。

    3.3 、 配置步骤

    3.3.1、导入相关坐标

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.1.6.RELEASE</version>
    </dependency>
    

    3.3.2、使用@RunWith 注解替换原有运行器

    @RunWith(SpringJUnit4ClassRunner.class)
    public class AccountServiceTest {}
    

    3.3.3、使用@ContextConfiguration 指定 spring 配置文件的位置

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringCinfiguration.class)
    public class AccountServiceTest {}
    

    @ContextConfiguration 注解:
    locations 属性:用于指定配置文件的位置。如果是类路径下,需要用 classpath:表明
    classes 属性:用于指定注解的类。当不使用 xml 配置时,需要用此属性指定注解类的位置。

    3.3.4、使用@Autowired

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringCinfiguration.class)
    public class AccountServiceTest {
    
        @Autowired
        private IAccountService as;
    }
    

    完整代码

    package test;
    
    import config.SpringCinfiguration;
    import org.apache.commons.dbutils.QueryRunner;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    import wf.domain.Account;
    import org.junit.Test;
    import wf.service.IAccountService;
    
    import java.util.List;
    
    /**
     * 使用junit进行单元测试
     * spring整合junit的配置
     *  1.导入spring整合junit的jar
     *  2.
     */
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringCinfiguration.class)
    public class AccountServiceTest {
    
        @Autowired
        private IAccountService as;
        @Test
        public void testQuery(){
            //1.加载配置文件
            AnnotationConfigApplicationContext ca = new AnnotationConfigApplicationContext(SpringCinfiguration.class);
            //2.获取服务层对象
            QueryRunner as = ca.getBean("runner", QueryRunner.class);
            QueryRunner as1 = ca.getBean("runner", QueryRunner.class);
            System.out.println(as ==as1);
    
        }
        @Test
        public void testFindAll(){
           /* //1.加载配置文件
            //, JdbcConfig.class
            AnnotationConfigApplicationContext ca = new AnnotationConfigApplicationContext(SpringCinfiguration.class);
            //2.获取服务层对象
            IAccountService as = ca.getBean("accountService", IAccountService.class);*/
            //3.根据对象执行方法
            List<Account> accounts = as.findAllAccount();
            for (Account account:accounts){
                System.out.println(account);
            }
        }
    
        @Test
        public void testFindById(){
           /* //1.加载配置文件
            AnnotationConfigApplicationContext ca = new AnnotationConfigApplicationContext(SpringCinfiguration.class);
            //2.获取服务层对象
            IAccountService as = ca.getBean("accountService", IAccountService.class);*/
            //3.根据对象执行方法
            Account a = as.findOneById(3);
            System.out.println(a);
        }
    
        @Test
        public void testSave(){
            Account account = new Account();
            account.setName("gx");
            account.setMoney(1234.0f);
            /*//1.加载配置文件
            AnnotationConfigApplicationContext ca = new AnnotationConfigApplicationContext(SpringCinfiguration.class);
            //2.获取服务层对象
            IAccountService as = ca.getBean("accountService", IAccountService.class);*/
            //3.根据对象执行方法
            as.saveAccount(account);
        }
    
        @Test
        public void testUpdate(){
            Account account = new Account();
            account.setId(4);
            account.setName("wf");
            account.setMoney(1234.0f);
           /* //1.加载配置文件
            AnnotationConfigApplicationContext ca = new AnnotationConfigApplicationContext(SpringCinfiguration.class);
            //2.获取服务层对象
            IAccountService as = ca.getBean("accountService", IAccountService.class);*/
            //3.根据对象执行方法
            as.updateAccount(account);
        }
        @Test
        public void testDelete(){
           /* //1.加载配置文件
            AnnotationConfigApplicationContext ca = new AnnotationConfigApplicationContext(SpringCinfiguration.class);
            //2.获取服务层对象
            IAccountService as = ca.getBean("accountService", IAccountService.class);*/
            //3.根据对象执行方法
            as.deleteAccount(8);
        }
    }
    

    也可以在xml配置文件项目中使用此方法整合junit

  • 相关阅读:
    .NET Core VS Code 环境配置
    系统架构正交分解法
    面向接口可扩展框架之“Mvc扩展框架及DI”
    发送和接收数据包
    Easticsearch通信方式_API
    Web Api集成Swagger
    NET MVC RazorEngine 解析模板生成静态页
    NET单元测试的艺术
    NET平台机器学习组件-Infer.NET
    ActiveMQ相关背景(转)
  • 原文地址:https://www.cnblogs.com/wf614/p/11673824.html
Copyright © 2011-2022 走看看