Java框架Spring

【Spring】06 - Spring 注解

2020-09-14  本文已影响0人  itlu

1. 使用纯注解配置

  1. 初始化一个项目 , 在该项目的基础上实现纯注解配置。

1.1 @Configuration

  1. 作用:指定当前类是一个配置类。

  2. 案例: 创建一个配置类

@Configuration
@ComponentScan("com.lyp")
public class BeanConfig {
...
}
  1. 细节 : 当前配置类作为AnnotationConfigurationApplicationContext对象创建的参数时,该注解可以不写。

1.2 @ComponentScan

  1. 作用:用于通过注解指定spring创建容器是需要扫描的包。

  2. 属性:

  • value :它和 basePackages 的作用都是一样的,都是用于配置创建容器时需要扫描的包。
  • basePackages: 它和 value 的作用都是一样的,都是用于配置创建容器时需要扫描的包。
  1. 使用此注解就等同于在xml中配置了:
 <!--配置注解需要扫描的包 , 高告知Spring 在创建容器时需要扫描的包-->
    <context:component-scan base-package="com.lyp"/>

1.3 @Bean

  1. 作用 :用于将当前方法的返回值作为Bean对象存入SpringIOC容器中。

  2. 属性 :

  • name:用于指定bean的id,当不写时,默认值就是当前方法的名称。
  1. 细节:当我们使用注解配置方法时,如果方法有参数Spring框架会去容器中查找有没有可用的bean对象,查找方式和 @Autowired 注解的作用是一样的。

  2. @Bean注解配置案例 :

@Configuration
@ComponentScan("com.lyp")
public class SpringConfiguration {

    /**
     * 创建!QueryRunner对象 ,使用@Bean对象将其交给Spring容器管理
     *
     * @param dataSource
     * @return
     */
    @Bean
    public QueryRunner createQueryRunner(DataSource dataSource) {
        return new QueryRunner(dataSource);
    }

    /**
     * 创建数据源
     *
     * @return
     */
    @Bean
    public DataSource createDataSource() {
        try {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass("com.mysql.jdbc.Driver");
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/spring");
            dataSource.setUser("root");
            dataSource.setPassword("root");
            return dataSource;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

1.4 AnnotationConfigurationApplicationContext 实现类

  1. AnnotationConfigurationApplicationContext 该实现类主要用于纯注解配置,加载配置类。

  2. 修改测试类代码 :

  public class SpringIOCAccountTest {

    private ApplicationContext ac;
    private AccountService as;


    @Before
    public void init() {
        // ac = new ClassPathXmlApplicationContext("bean.xml");
        ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        as = ac.getBean("accountService", AccountService.class);
    }
  ... 
 }
修改配置方式
  1. 此时便可以删除bean.xml配置文件。

1.5 @Import

  1. 作用:用于导入其他的配置类;

  2. 属性

  • value: 用于指定其他配置类的字节码。当我们使用 @Import 注解之后有 @Import 注解的类就是主配置(父配置)类。而导入的都是子配置类。
  1. 需求:创建一个配置类不加任何注解,但是可以使用。 此时就需要将该配置类的字节码作为参数传递给ApplicationContext 的实现类 AnnotationConfigurationApplicationContext(配置类.class)
public class JdbcConfiguration { ... }
  1. 需求:创建一个配置类不加任何注解,但是可以使用。简单的方式:在主配置文件中使用 @Import(配置类.class)注解将该配置类引入作为其子配置类。
public class JdbcConfiguration { ... }
@Configuration
@ComponentScan("com.lyp")
@Import(JdbcConfiguration.class)
public class SpringConfiguration {
}

1.6 @PropertySource

  1. 作用:用于指定Properties文件的位置。

  2. 属性:

  • value: 指定文件的名称和路径。关键字:classpath 表示类路径。

1.6.1 @PropertySource@Value 搭配使用 :

  1. resources目录下创建一个jdbcConfiguration.properties 文件,编写数据源配置信息:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=root
  1. 使用@PropertySource 注解在主配置文件中引入配置文件:
@Configuration
@ComponentScan("com.lyp")
@Import(JdbcConfiguration.class)
@PropertySource("classpath:JdbcConfiguration.properties")
public class SpringConfiguration {
}
  1. 在子配置文件中定义相应的属性,并使用 @Value("${}") : 引入属性值:
public class JdbcConfiguration {

    @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.7 @Qualifier 在方法的形参中可以单独使用

  1. 可以解决一个接口有多个实现类的方式,多个实现类在一个配置类中进行使用。此时根据类型自动注入可能就会显得无力。

  2. 创建一个新的数据库 ,并导入新的数据到数据库中 :

CREATE DATABASE spring2 CHARACTER SET utf8;
USE spring2;
CREATE TABLE account(
    id INT PRIMARY KEY AUTO_INCREMENT,
    NAME VARCHAR(40),
    money FLOAT
)CHARACTER SET utf8 COLLATE utf8_general_ci;

INSERT INTO account(NAME,money) VALUES('aaa',1000);
INSERT INTO account(NAME,money) VALUES('bbb',1000);
INSERT INTO account(NAME,money) VALUES('ccc',1000);
  1. 再编写一个数据源配置方法,并使用 @Bean注解指定name :
 /**
     * 创建数据源
     *
     * @return
     */
    @Bean(name = "dataSource")
    public DataSource createDataSource() {
        try {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass(driver);
            dataSource.setJdbcUrl(url);
            dataSource.setUser(username);
            dataSource.setPassword(password);
            return dataSource;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }


    /**
     * 在创建一个数据源 用于测试 在一个接口有多个实现类时 ,而且实现类同时出现在一个配置类中时。不能自动按照类型注入。此时该如何解决
     * @return
     */
    @Bean(name = "db2")
    public DataSource createDataSource2() {
        try {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass(driver);
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/spring2");
            dataSource.setUser(username);
            dataSource.setPassword(password);
            return dataSource;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
  1. 以上代码执行是不会出现冲突的:因为在创建 QueryRunner 对象是使用的形参叫 dataSource 和 第一个数据源对象 @Beanname 属性是一致的。在不能根据类型进行选择之后,会以形参的名称作为key在容器中查找对应的对象。但是如果将两个数据源对象的 @Beanname 都改了和创建 QueryRunner 对象传递的形参名称不一致时就会出现冲突:
配置类中包含两个相同的类型对象,而且属性名称这两对象的 key 不一致时 出现报错
  1. 解决:使用@Qualifier注解明确指定使用哪个对象 :
    解决冲突:指定明确

2. 测试 QueryRunner 是否单例

  1. 我们使用完纯注解方式配置完成之后,是否会有疑惑,我们创建的 QueryRunner 对象是单例对象还是多例对象呢?

  2. 进行测试 :

  /**
     * 创建!QueryRunner对象 ,使用@Bean对象将其交给Spring容器管理
     *
     * @param dataSource
     * @return
     */
    @Bean("runner")
    public QueryRunner createQueryRunner(DataSource dataSource) {
        return new QueryRunner(dataSource);
    }
   @Test
    public void testQueryRunner() {
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);
       QueryRunner runner1 = (QueryRunner) ac.getBean("runner");
       QueryRunner runner2 = (QueryRunner) ac.getBean("runner");
        System.out.println(runner1 == runner2);
    }
测试结果

2.1 @Scope 注解

  1. 使用 @Scope注解标注创建QueryRunner对象的方法,设置作用范围为 prototype (多例的)。
   @Bean("runner")
    @Scope("prototype")
    public QueryRunner createQueryRunner(DataSource dataSource) {
        return new QueryRunner(dataSource);
    }
测试结果

3. Spring整合Junit

  1. 应用程序的入口:main方法。

  2. Juni单元测试中,没有main方法也能执行? 其实junit集成了一个main方法。该方法就会判断当前测试类中哪些方法有 @Test 注解。junit就让有@Test注解的方法执行。

  3. junit不会管我们是否采用spring框架:在执行测试方法时,junit 根据不知道我们是不是使用了 spring 框架。所以也不会为我们读取配置文件/配置类创建Spring容器。

  4. 由以上三点可知:当测试方法执行时,没有 IOC 容器,就算写了 @Autowired 注解,也无法实现注入。此时使用该对象将出现 空指针异常

3.1 Spring整合Junit的配置 (修改纯注解的)

  1. 导入Spring整合Junit配置的jar包依赖:
    <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
  1. 使用Junit提供的一个注解把原来的main方法替换了,替换成spring提供的。 @Runwith(SpringJUnit4ClassRunner.class);
  @RunWith(SpringJUnit4ClassRunner.class)
public class SpringIOCAccountTest {...}
  1. 告知Spring的运行器,spring和ioc创建是基于xml还是注解的。并且说明位置。@ContextConfiguration()
  • locations: 指定xml文件位置,加上classpath关键字表示在类路径下。
  • classes: 指定注解类所在位置。配置类的字节码。
  • 当我们使用 spring5.x 版本的时候,要求Junit的jar包必须是 4.12 及以上。
由于Junit的版本是 4.10 出现报错 Spring-test的版本引入的版本高于 Spring-context的版本出现该错误!
  1. 经过以上配置之后便可以不在使用如下比较繁琐的配置 :
    public class SpringIOCAccountTest {

    private ApplicationContext ac;
    private AccountService as;


    @Before
    public void init() {
        ac = new ClassPathXmlApplicationContext("bean.xml");
        as = ac.getBean("accountService", AccountService.class);
    }

    /**
     * 查询所有账户信息
     */
    @Test
    public void testyFindAll() {
        List<Account> accounts = as.findAll();
        for (Account account : accounts) {
            System.out.println(account);
        }
    }
  ...
}
  1. 而是使用如下简单的配置 :
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class SpringIOCAccountTest {

    @Autowired
    private AccountService as;

    /**
     * 查询所有账户信息
     */
    @Test
    public void testyFindAll() {
        List<Account> accounts = as.findAll();
        for (Account account : accounts) {
            System.out.println(account);
        }
    }

3.2 Spring整合Junit的配置 (修改xml配置的)

  1. 步骤基本和上面相同 :不同的是这里配置文件的指定方式@ContextConfiguration(locations = "classpath:bean.xml")
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class SpringIOCAccountTest {

    @Autowired
    private AccountService as;


    /**
     * 查询所有账户信息
     */
    @Test
    public void testyFindAll() {
        List<Account> accounts = as.findAll();
        for (Account account : accounts) {
            System.out.println(account);
        }
    }
... 
}
Demo码云地址
  1. Demo码云地址:Spring基于纯注解的account表+dbutils的增删查改
上一篇下一篇

猜你喜欢

热点阅读