前言

Spring3.0之前要使用Spring必须要有一个xml配置文件,而Spring3.0之后注解慢慢登上舞台,通过注解@Configuration和@Bean可以完全搞定。此时,注解和xml配置形成了相互协作与竞争的关系。随着Springboot的推广,注解的使用在Spring中大放光彩,xml的辉煌一去不返。通过注解,简化了配置,提升了编码效率。

Spring 3.0新增了另外两个实现类:AnnotationConfigApplicationContext 和 AnnotationConfigWebApplicationContext。它们是为注解而生,直接依赖于注解作为容器配置信息来源的IoC容器初始化类。AnnotationConfigWebApplicationContext是AnnotationConfigApplicationContext的web版本,其用法与后者相比几乎没有什么差别。

今天这篇文章带大家学习@Configuration和@Bean的使用,并通过具体的实例体验一下注解的方便快捷。如果你的项目中还未曾使用此类注解,说明你的技术栈已经在被淘汰的边缘。

基于Java类的配置选项

Spring 3.0引入了注解,配置文件的载体就从xml文件转换为了Java类,Java类就是一个普通的类,除了命名建议以“**Config”结尾方便识别外,Spring对其有一定的约定条件。

  • 配置类不能是 final 类(没法动态代理)。

  • 配置类必须是非本地的(即不能将配置类定义在其他类的方法内部,不能是private)。

  • 配置类必须有一个无参构造函数。

基本使用方法

符合上述条件的类,就可以使用@Configuration来进行注解,表示这个类可以使用Spring IoC容器作为bean定义的来源。@Bean注解在该类的方法上,AnnotationConfigApplicationContext将配置类中标注了@Bean的方法的返回值识别为Spring Bean,并注册到容器中,归入IoC容器管理。

@Configuration的作用等价于XML配置中的标签,@Bean的作用等价于XML配置中的标签。下面代码完成了一个简单的示例。

  1. @Configuration

  2. public class DataSourceConfig {

  3.    @Bean

  4.    public MysqlDataSource mysqlDataSource() {

  5.        return new MysqlDataSource();

  6.    }

  7.  

  8.    @Bean(name = "oracleDataSource")

  9.    public OracleDataSource oracleDataSource() {

  10.        return new OracleDataSource();

  11.    }

  12. }

Spring在解析该类时,会识别出标注@Bean的所有方法,执行并将方法的返回值(MysqlDataSource和OracleDataSource对象)注册到IoC容器中。默认情况下,方法名即为Bean的名字。与以上配置等价的XML配置如下:

  1. <bean id="mysqlDataSource" class="**.MysqlDataSource"/>

  2. <bean id="oracleDataSource" class="**.OracleDataSource"/>

@Configuration的属性

@Configuration的定义代码:

  1. @Target({ElementType.TYPE})

  2. @Retention(RetentionPolicy.RUNTIME)

  3. @Documented

  4. @Component

  5. public @interface Configuration {

  6.    String value() default "";

  7. }

@Configuration注解本身定义时被@Component标注了,因此本质上来说@Configuration也是一个@Component,只不过我们在具体使用的过程中基本用不到它的实例化对象。

@Bean属性

@Bean注解的具体代码定义:

  1. @Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})

  2. @Retention(RetentionPolicy.RUNTIME)

  3. @Documented

  4. public @interface Bean {

  5.    @AliasFor("name")

  6.    String[] value() default {};

  7.  

  8.    @AliasFor("value")

  9.    String[] name() default {};

  10.  

  11.    Autowire autowire() default Autowire.NO;

  12.  

  13.    String initMethod() default "";

  14.  

  15.    String destroyMethod() default "(inferred)";

  16. }

可以看出@Bean具有以下属性:

  • name :指定一个或者多个Bean的名字。等价于XML配置中的name属性,示例中的@Bean(name = "oracleDataSource")。

  • initMethod:容器在初始化完Bean之后,会调用该属性指定的方法。等价于XML配置中的init-method属性。

  • destroyMethod:该属性与initMethod功能相似,在容器销毁Bean之前,会调用该属性指定的方法。等价于XML配置中的destroy-method属性。

  • autowire:指定Bean属性的自动装配策略,取值是Autowire类型的三个静态属性。Autowire.BYNAME,Autowire.BYTYPE,Autowire.NO。与XML配置中的autowire属性的取值相比,少了constructor,因为 constructor在这里已经没有意义了。

@Bean默认是单例模式,并且没有提供指定作用域的属性,可以通过@Scope来实现该功能。

  1. @Bean

  2. @Scope("prototype")

  3. public MysqlDataSource mysqlDataSource() {

  4.    return new MysqlDataSource();

  5. }

Spring扫描加载

当配置完Spring扫描指定包及其子包中的类时,会识别所有标记了@Component、@Controller、@Service、@Repository注解的类,由于@Configuration注解本身也用@Component标注了,Spring将能够识别出 @Configuration标注类。

实例

现在对上面的示例进行单元测试,其中MysqlDataSource和OracleDataSource类中分别提供了打印日志的方法:

  1. public void print(){

  2.    System.out.println("I am MysqlDataSource!");

  3. }

  4. public void print(){

  5.    System.out.println("I am OracleDataSource!");

  6. }

指定单元测试的代码如下:

  1. public class ConfigBeanTest {

  2.  

  3.    @Test

  4.    public static void testGetBean() {

  5.        ApplicationContext ctx = new AnnotationConfigApplicationContext(DataSourceConfig.class);

  6.        MysqlDataSource mysqlDataSource = ctx.getBean(MysqlDataSource.class);

  7.        mysqlDataSource.print();

  8.    }

  9. }

当执行单元测试,成功打印出:

  1. I am MysqlDataSource!

实战技巧

如果我们在DataSourceConfig中再添加一个方法,这个方法用到了前面实例化的两个bean对象,那么该如何处理?常规的想法是在DataSourceConfig中添加如下代码,然后再直接使用属性:

  1. @Autowired

  2. private MysqlDataSource mysqlDataSource;

其实不必如此,直接调用方法mysqlDataSource()方法即可。比如,需要新增下面类的实例化:

  1. public class CommonDataSource {

  2.  

  3.    private MysqlDataSource mysqlDataSource;

  4.  

  5.    private OracleDataSource oracleDataSource;

  6.    // 省略getter/setter方法

  7. }

那么,我们只用在DataSourceConfig中添加如下代码即可:

  1. @Bean

  2. public CommonDataSource commonDataSource() {

  3.    CommonDataSource commonDataSource = new CommonDataSource();

  4.    commonDataSource.setMysqlDataSource(mysqlDataSource());

  5.    commonDataSource.setOracleDataSource(oracleDataSource());

  6.    return commonDataSource;

  7. }

注意:set方法内直接set的是mysqlDataSource()。

小结

这节课我们讲解了Spring注解中@Configuration和@Bean使用方法,在Springboot中集成其他三方框架时,这种写法使用的越来越普遍。如果一时无法转换思维,可对照xml文件的配置进行逐一切换过来,比如xml要定义一个bean,那么用注解就是@Bean注解一个方法。如果方法里面的参数还有其他的依赖,那就采用上面介绍的实战技巧依次追踪set对应的参数,并将其先通过@Bean实例化。

本节代码中使用到了@Configuration和@Bean的源代码,如果你对注解的使用不太熟悉可参考《一篇文章,全面掌握Java自定义注解(Annontation)》一文。