夯实spring(十七): @Configration、@Bean注解详解

之前我们都是通过xml的方式定义bean,里面会写很多bean元素,然后spring启动的时候,就会读取bean.xml配置文件,然后解析这些配置,然后会将这些bean注册到spring容器中,供使用者使用。

jdk1.5里面有了注解的功能,spring就将注解加了进来,让我们可以通过注解的方式来定义bean,用起来能达到xml中定义bean一样的效果,并且更简洁一些,这里面需要用到的注解就@Configuration注解和@Bean注解。

1,@Configuration注解

@Configuration这个注解可以加在类上,让这个类的功能等同于一个bean.xml配置文件

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
    @AliasFor(
        annotation = Component.class
    )
    String value() default "";

    boolean proxyBeanMethods() default true;
}

使用:

@Configuration
public class config {
}

相当于如下的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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.3.xsd">
</beans>

使用bean.xml时我们都是使用ClassPathXmlApplicationContext 来加载bean.xml

String beanXml = "bean.xml";
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(beanXml);

使用@Configuration我们使用AnnotationConfigApplicationContext来加载

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(config.class);
    }
}

上面的config类中没有任何内容,相当于一个空的xml配置文件,此时我们要在config类中注册bean,那么我们就要用到@Bean注解。

小结:

  • 在类上使用@Configuration注解
  • 通过AnnotationConfigApplicationContext容器来加@Configuration注解修饰的类

2,@Bean注解

这个注解类似于bean.xml配置文件中的bean元素,用来在spring容器中注册一个bean。

@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Bean {
    @AliasFor("name")
    String[] value() default {};

    @AliasFor("value")
    String[] name() default {};

    /** @deprecated */
    @Deprecated
    Autowire autowire() default Autowire.NO;

    boolean autowireCandidate() default true;

    String initMethod() default "";

    String destroyMethod() default "(inferred)";
}

@Bean注解用在方法上,表示通过方法来定义一个bean,默认将方法名称作为bean名称,将方法返回值作为bean对象,注册到spring容器中。

来个案列:

@Configuration
public class Config {

    //bean名称为方法默认值:car
    @Bean
    public Car car(){
        return new Car();
    }

    //bean名称通过value指定了:carBean
    @Bean("carBean")
    public Car car2(){
        return new Car();
    }


    //bean名称为:car3Bean,2个别名:["car3BeanAlias1", "car3BeanAlias2"]
    @Bean({"car3Bean", "car3BeanAlias1", "car3BeanAlias2"})
    public Car Car3() {
        return new Car();
    }
}

测试输出:

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        for (String name : context.getBeanDefinitionNames()) {
            String[] aliases = context.getAliases(name);
            System.out.println(String.format("bean名称:%s,别名:%s,bean对象:%s",
                    name,
                    Arrays.asList(aliases),
                    context.getBean(name)));
        }
    }
}
bean名称:org.springframework.context.annotation.internalConfigurationAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.ConfigurationClassPostProcessor@64c87930
bean名称:org.springframework.context.annotation.internalAutowiredAnnotationProcessor,别名:[],bean对象:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@400cff1a
bean名称:org.springframework.context.annotation.internalCommonAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@275710fc
bean名称:org.springframework.context.event.internalEventListenerProcessor,别名:[],bean对象:org.springframework.context.event.EventListenerMethodProcessor@525f1e4e
bean名称:org.springframework.context.event.internalEventListenerFactory,别名:[],bean对象:org.springframework.context.event.DefaultEventListenerFactory@75f9eccc
bean名称:config,别名:[],bean对象:com.chen.Config$$EnhancerBySpringCGLIB$$fa7a250c@52aa2946
bean名称:car,别名:[],bean对象:com.chen.Car@4de5031f
bean名称:carBean,别名:[],bean对象:com.chen.Car@67e2d983
bean名称:car3Bean,别名:[car3BeanAlias1, car3BeanAlias2],bean对象:com.chen.Car@5d47c63f

可以看出,有个名称为config的bean,是Config这个类型,所以被@Configuration修饰的类,也被注册到spring容器中了

3,去掉@Configuration会怎么样

我们把Config的@Configuration去掉

public class Config {

    //bean名称为方法默认值:car
    @Bean
    public Car car(){
        return new Car();
    }

    //bean名称通过value指定了:carBean
    @Bean("carBean")
    public Car car2(){
        return new Car();
    }


    //bean名称为:car3Bean,2个别名:["car3BeanAlias1", "car3BeanAlias2"]
    @Bean({"car3Bean", "car3BeanAlias1", "car3BeanAlias2"})
    public Car Car3() {
        return new Car();
    }
}

直接运行和加了@Configuration对比:

加了@Configuration:

bean名称:org.springframework.context.annotation.internalConfigurationAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.ConfigurationClassPostProcessor@64c87930
bean名称:org.springframework.context.annotation.internalAutowiredAnnotationProcessor,别名:[],bean对象:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@400cff1a
bean名称:org.springframework.context.annotation.internalCommonAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@275710fc
bean名称:org.springframework.context.event.internalEventListenerProcessor,别名:[],bean对象:org.springframework.context.event.EventListenerMethodProcessor@525f1e4e
bean名称:org.springframework.context.event.internalEventListenerFactory,别名:[],bean对象:org.springframework.context.event.DefaultEventListenerFactory@75f9eccc
bean名称:config,别名:[],bean对象:com.chen.Config$$EnhancerBySpringCGLIB$$fa7a250c@52aa2946
bean名称:car,别名:[],bean对象:com.chen.Car@4de5031f
bean名称:carBean,别名:[],bean对象:com.chen.Car@67e2d983
bean名称:car3Bean,别名:[car3BeanAlias1, car3BeanAlias2],bean对象:com.chen.Car@5d47c63f

不加@Configuration:

bean名称:org.springframework.context.annotation.internalConfigurationAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.ConfigurationClassPostProcessor@23fe1d71
bean名称:org.springframework.context.annotation.internalAutowiredAnnotationProcessor,别名:[],bean对象:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@28ac3dc3
bean名称:org.springframework.context.annotation.internalCommonAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@32eebfca
bean名称:org.springframework.context.event.internalEventListenerProcessor,别名:[],bean对象:org.springframework.context.event.EventListenerMethodProcessor@4e718207
bean名称:org.springframework.context.event.internalEventListenerFactory,别名:[],bean对象:org.springframework.context.event.DefaultEventListenerFactory@1d371b2d
bean名称:config,别名:[],bean对象:com.chen.Config@543c6f6d
bean名称:car,别名:[],bean对象:com.chen.Car@13eb8acf
bean名称:carBean,别名:[],bean对象:com.chen.Car@51c8530f
bean名称:car3Bean,别名:[car3BeanAlias1, car3BeanAlias2],bean对象:com.chen.Car@7403c468

可以发现我们的Config配置类不一样了:

加了@Configuration:

bean名称:config,别名:[],bean对象:com.chen.Config$$EnhancerBySpringCGLIB$$fa7a250c@52aa2946

不加@Configuration:

bean名称:config,别名:[],bean对象:com.chen.Config@543c6f6d
  • 可以看出:有没有@Configuration注解,@Bean都会起效,都会将@Bean修饰的方法作为bean注册到容器中
  • 被@Configuration修饰的bean最后输出的时候带有EnhancerBySpringCGLIB的字样,而没有@Configuration注解的bean没有Cglib的字样;有EnhancerBySpringCGLIB字样的说明这个bean被cglib处理过的,变成了一个代理对象。

4,加不加@Configuration的区别

通常情况下,bean之间是有依赖关系的,创建个有依赖关系的bean,通过这个案例可以看出根本的区别。

public class Server1 {
}
public class Server2 {
    private Server1 server1;

    public Server2(Server1 server1){
        this.server1 = server1;
    }

    @Override
    public String toString() {
        return "Server2{" +
                "server1=" + server1 +
                '}';
    }
}
@Configuration
public class Config {

    @Bean
    public Server1 server1() {
        System.out.println("创建Server1"); //@0
        return new Server1();
    }
    @Bean
    Server2 server2_1() {
        System.out.println("创建Server2_1");
        Server1 serviceA = this.server1(); //@1
        return new Server2(serviceA);
    }
    @Bean
    Server2 server2_2() {
        System.out.println("创建server2_2");
        Server1 serviceA = this.server1(); //@2
        return new Server2(serviceA);
    }
}

测试输出:

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        for (String name : context.getBeanDefinitionNames()) {
            //别名
            String[] aliases = context.getAliases(name);
            System.out.println(String.format("bean名称:%s,别名:%s,bean对象:%s",
                    name,
                    Arrays.asList(aliases),
                    context.getBean(name)));
        }
    }
}
创建Server1
创建Server2_1
创建server2_2
bean名称:org.springframework.context.annotation.internalConfigurationAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.ConfigurationClassPostProcessor@55040f2f
bean名称:org.springframework.context.annotation.internalAutowiredAnnotationProcessor,别名:[],bean对象:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@64c87930
bean名称:org.springframework.context.annotation.internalCommonAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@400cff1a
bean名称:org.springframework.context.event.internalEventListenerProcessor,别名:[],bean对象:org.springframework.context.event.EventListenerMethodProcessor@275710fc
bean名称:org.springframework.context.event.internalEventListenerFactory,别名:[],bean对象:org.springframework.context.event.DefaultEventListenerFactory@525f1e4e
bean名称:config,别名:[],bean对象:com.chen.Config$$EnhancerBySpringCGLIB$$f7271c@75f9eccc
bean名称:server1,别名:[],bean对象:com.chen.Server1@52aa2946
bean名称:server2_1,别名:[],bean对象:Server2{server1=com.chen.Server1@52aa2946}
bean名称:server2_2,别名:[],bean对象:Server2{server1=com.chen.Server1@52aa2946}
  • 前三行可以看出,被@Bean修饰的方法都只被调用了一次。
  • 最后三行中可以看出都是同一个server1对象,都是Server1@52aa2946这个实例

被@Configuration修饰的类,spring容器中会通过cglib给这个类创建一个代理,代理会拦截所有被@Bean修饰的方法,默认情况(bean为单例)下确保这些方法只被调用一次,从而确保这些bean是同一个bean,即单例的。

我们把@Configuration去掉再运行输出:

创建Server1
创建Server2_1
创建Server1
创建server2_2
创建Server1
bean名称:org.springframework.context.annotation.internalConfigurationAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.ConfigurationClassPostProcessor@74a10858
bean名称:org.springframework.context.annotation.internalAutowiredAnnotationProcessor,别名:[],bean对象:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@23fe1d71
bean名称:org.springframework.context.annotation.internalCommonAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@28ac3dc3
bean名称:org.springframework.context.event.internalEventListenerProcessor,别名:[],bean对象:org.springframework.context.event.EventListenerMethodProcessor@32eebfca
bean名称:org.springframework.context.event.internalEventListenerFactory,别名:[],bean对象:org.springframework.context.event.DefaultEventListenerFactory@4e718207
bean名称:config,别名:[],bean对象:com.chen.Config@1d371b2d
bean名称:server1,别名:[],bean对象:com.chen.Server1@543c6f6d
bean名称:server2_1,别名:[],bean对象:Server2{server1=com.chen.Server1@13eb8acf}
bean名称:server2_2,别名:[],bean对象:Server2{server1=com.chen.Server1@51c8530f}

可以看到

  • server1()方法被调用了3次
  • Config这个bean没有代理效果了
  • 最后3行可以看出,几个server1对象都是不一样的

总结

  • @Configuration注解修饰的类,会被spring通过cglib做增强处理,通过cglib会生成一个代理对象,代理会拦截所有被@Bean注解修饰的方法,可以确保一些bean是单例的
  • 不管@Bean所在的类上是否有@Configuration注解,都可以将@Bean修饰的方法作为一个bean注册到spring容器中
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值