Spring 从入门到精通系列 08 —— 纯注解方式实现 IOC 案例与 Junit 整合

本文针对Spring使用纯注解开发,并对 Spring 整合 Junit 做了一定的分析。

在这里插入图片描述



一、 Spring中的新注解

  上一讲基于注解开发的案例,还是需要 bean.xml 文件,即没有完全脱离配置文件,本节介绍 Spring 新注解实现完全脱离配置文件。
上一讲剩余的配置如下所示:

<?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 https://www.springframework.org/schema/context/spring-context.xsd">

    <!--配置QueryRunner-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <constructor-arg name="ds" ref="dataSource" />
    </bean>

    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/springdb" />
        <property name="user" value="root" />
        <property name="password" value="123456" />
    </bean>

    <context:component-scan base-package="com.axy" />
</beans>

1.1 Configuration 注解

  • 作用: 指定当前类是一个配置类

  • 细节: 当配置类作为 AnnotationConfigApplicationContext 对象创建的参数时,该注解可以不写。

    // SpringConfiguration是配置类的类名
    ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
    

1.2 ComponentScan 注解

  • 作用: 用于通过注解指定spring在创建容器时要扫描的包
  • 属性: value 和 basePackage的作用是一样的,都是用于指定创建容器是要扫描的包。我们使用此注解就等同于在xml中配置:
    <context:component-scan base-package="com.axy"></context:component-scan>
    

1.3 Bean 注解

  以下两种方式,得到 queryRunner 对象的方式区别在于 bean 标签配置,创建反射对象后,会放入 IOC 容器中,而 createQueryRunner 得到的对象不会放入 IOC 容器中(因为加不了 Autowired 注解),@Bean 注解可以解决此问题。
在这里插入图片描述

  • 作用: 用于把当前方法的返回值作为 bean 对象存入 spring 的 IOC 容器中
  • 属性: name 用于指定 bean 的 id。当不写时,默认值时当前方法的名称。
  • 细节: 当我们使用注解配置方法时,如果方法有参数,spring 框架会去容器中查找有没有可用的 bean 对象,查找的方式和 Autowired 注解的作用是一样的。
    如:createQueryRunner 方法中的参数 dataSource,会先去拿数据类型 DataSource 去容器中匹配已经注入的数据类型,如果匹配到唯一的 bean 对象类型,则注入成功。如果匹配到多个变量类型,还需进一步比较变量名称 dataSource。

通过以上三个注解,我们就可实现完全脱离 bean.xml 的方式。新建配置类,代码如下:

@Configuration
@ComponentScan(basePackages = "com.axy")
public class SpringConfiguration {

    @Bean(name = "runner")
    @Scope("prototype") // 多例对象
    public QueryRunner createQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }

    @Bean(name = "dataSource")
    public DataSource createDataSource(){
        try{
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass("com.mysql.jdbc.Driver");
            ds.setJdbcUrl("jdbc:mysql://localhost:3306/springdb");
            ds.setUser("root");
            ds.setPassword("123456");
            return ds;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

测试方法:

@Test
public void testQueryRunner(){
    //获取容器
    ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
    //获取service层对象
    IAccountService accountService = (IAccountService) ac.getBean("accountService");
    //执行查询方法
    Account account = accountService.findAccountById(1);
    System.out.println(account);
}

测试结果:
在这里插入图片描述


1.4 Import 注解

  • 作用: 用于导入其他配置类

  • 属性: 用于指定其他配置类下的字节码。当我们使用 @Import 注解之后,有 @Import 注解的类就是父配置类,而导入的就是子配置类。

    在 SpringConfiguration.java 下

    //@Configuration // 使用 @Import注解后,可不用加 @Configuration
    @ComponentScan(basePackages = "com.axy")
    @Import(JdbcConfigAnother.class)
    public class SpringConfiguration {...}
    

    在新创建的类,用于存放配置信息 jdbcConfig.java 下

    @Configuration //可加可不加
    public class JdbcConfigAnother{...配置信息...}
    

    在 test 类下创建容器

    ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
    

1.5 PropertySource 注解

  • 作用: 用于指定 properties 文件的位置

  • 属性: value 用于指定文件的名称和路径;classpath 用于表示类路径下
    (这里我们举例,jdbcConfig.properties 在 resources 文件夹下)

    jdbcConfig.properties:

    jdbc.driver=com.mysql.cj.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/mybatisdb
    jdbc.username=root
    jdbc.password=123456
    

    父配置类:

    @Configuration
    @ComponentScan(basePackages = "com.axy")
    @Import(JdbcConfigAnother.class)
    @PropertySource("classpath:jdbcConfig.properties") // 项目一运行,resources 全在 class 路径下
    public class SpringConfiguration {
    }
    

    子配置类:

    /**
     * 用于与数据库做交互的配置类
     */
    @Configuration //可加可不加
    public class JdbcConfigAnother{
        @Value("${jdbc.driver}")
        private String driver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
    
        /**
         * 用于创建一个QueryRunner对象
         */
        @Bean(name = "runner")
        @Scope("prototype")
        public QueryRunner createQueryRunner(DataSource dataSource){
            return new QueryRunner(dataSource);
        }
    
        /**
         * 创建数据源对象
         */
        @Bean(name = "dataSource")
        public DataSource createDataSource1(){
            try{
                ComboPooledDataSource ds = new ComboPooledDataSource();
                ds.setDriverClass(driver);
                ds.setJdbcUrl(url);
                ds.setUser(username);
                ds.setPassword(password);
                return ds;
            }catch (Exception e){
                throw new RuntimeException(e);
            }
        }
    }
    

1.6 Qualifier 注解

当配置类需要配置多个数据库的时候,有以下两种解决方案。

  1. QueryRunner 的参数可以根据变量名称,如:ds2来指定注入的数据库配置源信息(如上文Bean 注解举例)

    @Bean(name = "runner")
    @Scope("prototype")
    public QueryRunner createQueryRunner(DataSource ds2){ //ds2表示指定创建 @Bean(name="ds2")的数据库源对象
        return new QueryRunner(ds2);
    }
    
  2. 用 Qualifier 注解进行单独配置:他在给类成员注入时不能单独(使用需和Autowired配合),但是在给方法参数注入时可以

    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;
    
        /**
         * 用于创建一个QueryRunner对象
         */
        @Bean(name="runner")
        @Scope("prototype")
        public QueryRunner createQueryRunner(@Qualifier("ds2") DataSource dataSource){
            return new QueryRunner(dataSource);
        }
    
        /**
         * 创建数据源对象
         */
        @Bean(name="ds1")
        public DataSource createDataSource(){
            try {
                ComboPooledDataSource ds = new ComboPooledDataSource();
                ds.setDriverClass(driver);
                ds.setJdbcUrl(url);
                ds.setUser(username);
                ds.setPassword(password);
                return ds;
            }catch (Exception e){
                throw new RuntimeException(e);
            }
        }
    
        @Bean(name="ds2")
        public DataSource createDataSource1(){
            try {
                ComboPooledDataSource ds = new ComboPooledDataSource();
                ds.setDriverClass(driver);
                ds.setJdbcUrl("jdbc:mysql://localhost:3306/mybatisdb2");
                ds.setUser(username);
                ds.setPassword(password);
                return ds;
            }catch (Exception e){
                throw new RuntimeException(e);
            }
        }
    }
    

二、Spring 整合 Junit (了解)

Spring 整合 Junit,只在测试工程师方面起到作用,更加关注功能的实现,而不会关注是否创建 ioc 容器。

2.1 问题分析

应用程序的入口:main方法

junit单元测试中,没有 main 方法也能执行

  1. junit 集成了 main 方法
  2. 该方法就会判断当前测试类中哪些方法有 @Test 注解
  3. junit 就让有 Test 注解的方法执行

junit不会管我们是否采用spring框架

  1. 在执行测试方法时,junit根本不知道我们是不是使用了spring框架
  2. 所以也就不会为我们读取配置文件/配置类创建spring核心容器

由以上三点可知 :当测试方法时,没有 ioc 容器,就算写了 Autowired 注解,也无法实现注入。那么我们需要解决的是,将原本不能加载 main 方法换掉,换成能加载的,从而实现创建容器。


2.2 Spring 整合 junit配置

  1. 导入 spring 整合 junit 的 jar(坐标)

    <dependency>
    	<groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    
  2. 使用 junit 提供的一个注解把原有的 main 方法替换了,替换成 spring 提供的 @Runwith

  3. 告知 Spring 的运行器,spring 和 ioc 创建是基于 xml 还是注解的,并且说明位置 @ContextConfiguration

    • localtions:指定 xml 文件的位置,加上 classpath 关键字,表示在类路径下
    • classes:指定注解所在位置

如:基于注解配置

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

   @Test
   public void testFindAll(){ // 测试查询所有方法
       List<Account> accounts = as.findAllAccount();
       for(Account account:accounts){
           System.out.println(account);
       }
   }
}

如:基于XML配置

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {
   @Autowired
   private IAccountService as = null;

   @Test
   public void testFindAll(){
       List<Account> accounts = as.findAllAccount();
       for(Account account:accounts){
           System.out.println(account);
       }
   }
}

注意:当我们使用 spring 5.x 版本的时候,要求 junit 的 jar 必须是 4.1.2 及以上


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Xiu Yan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值