@Configuration注解的作用

本文分析了Spring中@Configuration注解与@Bean注解的用途,重点强调了@Configuration确保Bean的单例特性以及其在配置和依赖管理上的优势。
摘要由CSDN通过智能技术生成

今天在复习的时候看到了一个问题:@bean的作用是在Spring容器注册bean,那在有和没有在@bean注解的类上加@Configuration有什么区别?

介绍

  1. @Configuration

    • 这个注解标记在一个类上,表明该类是Spring的一个配置类,即它包含了一系列通过@Bean注解的方法来定义Bean的配置信息。
    • 使用@Configuration标记的类会被Spring的组件扫描机制识别为配置类,并通过CGLIB代理或ASM字节码工具动态生成一个实现了BeanDefinitionRegistryPostProcessor接口的类,进一步处理配置类中声明的@Bean方法。
    • 当Spring容器初始化时,会执行配置类中的@Bean方法,生成相应的Bean定义并添加到Spring容器中。
  2. @Bean

    • 用于方法级别,指出某个方法的返回值应该作为一个Bean注册到Spring容器中。
    • 单独使用@Bean注解时,如果没有在配置类上加上@Configuration,那么这个Bean定义可能是局部的或者是通过@Import引入到全局容器中的。
    • @Bean方法位于非@Configuration标注的类中,Spring容器可能无法直接识别和处理这些Bean定义,除非你通过其他方式(比如XML配置或@ComponentScan配合@Import)将这些Bean纳入到Spring上下文中。

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

因此,@Configuration注解的主要作用就是使注册进容器的Bean是单例的。

代码示例

创建ServiceA和ServiceB两个实现类

public class ServiceA {}
public class ServiceB {
    private ServiceA serviceA;

    public ServiceB (ServiceA serviceA){
        this.serviceA = serviceA;
    }

    @Override
    public String toString() {
        return "ServiceB{" +
                "serviceA=" + serviceA +
                '}';
    }
}

然后在config中使用@Bean注解将方法注册入Spring容器中

@Configuration
public class configBean {
    @Bean
    public ServiceA serviceA() {
        System.out.println("调用serviceA()方法");
        return new ServiceA();
    }

    @Bean
    ServiceB serviceB1() {
        System.out.println("调用serviceB1()方法");
        ServiceA serviceA = this.serviceA();
        return new ServiceB(serviceA);
    }

    @Bean
    ServiceB serviceB2() {
        System.out.println("调用serviceB2()方法");
        ServiceA serviceA = this.serviceA();
        return new ServiceB(serviceA);
    }
    
}

在测试中对方法进行输出

@Test
    public void springTest(){
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(configBean.class);
        for (String beanName : context.getBeanDefinitionNames()) {
            //别名
            String[] aliases = context.getAliases(beanName);
            System.out.println(String.format("bean名称:%s,别名:%s,bean对象:%s",
                    beanName,
                    Arrays.asList(aliases),
                    context.getBean(beanName)));
        }
    }

输出结果

加了@configuration注解的输出结果
调用serviceA()方法
14:36:48.628 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'serviceB1'
调用serviceB1()方法
14:36:48.628 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'serviceB2'
调用serviceB2()方法
bean名称:org.springframework.context.annotation.internalConfigurationAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.ConfigurationClassPostProcessor@5aa9e4eb
bean名称:org.springframework.context.annotation.internalAutowiredAnnotationProcessor,别名:[],bean对象:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@6989da5e
bean名称:org.springframework.context.annotation.internalCommonAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@385c9627
bean名称:org.springframework.context.event.internalEventListenerProcessor,别名:[],bean对象:org.springframework.context.event.EventListenerMethodProcessor@139982de
bean名称:org.springframework.context.event.internalEventListenerFactory,别名:[],bean对象:org.springframework.context.event.DefaultEventListenerFactory@682b2fa
bean名称:configBean,别名:[],bean对象:cn.spring.test.config.configBean$$EnhancerBySpringCGLIB$$1dcc2b0a@217ed35e
bean名称:serviceA,别名:[],bean对象:cn.spring.test.service.ServiceA@7dcf94f8
bean名称:serviceB1,别名:[],bean对象:ServiceB{serviceA=cn.spring.test.service.ServiceA@7dcf94f8}
bean名称:serviceB2,别名:[],bean对象:ServiceB{serviceA=cn.spring.test.service.ServiceA@7dcf94f8}
不加@Configuration的输出结果
调用serviceA()方法
14:38:56.391 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'serviceB1'
调用serviceB1()方法
调用serviceA()方法
14:38:56.391 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'serviceB2'
调用serviceB2()方法
调用serviceA()方法
bean名称:org.springframework.context.annotation.internalConfigurationAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.ConfigurationClassPostProcessor@64cd705f
bean名称:org.springframework.context.annotation.internalAutowiredAnnotationProcessor,别名:[],bean对象:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor@9225652
bean名称:org.springframework.context.annotation.internalCommonAnnotationProcessor,别名:[],bean对象:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor@654f0d9c
bean名称:org.springframework.context.event.internalEventListenerProcessor,别名:[],bean对象:org.springframework.context.event.EventListenerMethodProcessor@6a400542
bean名称:org.springframework.context.event.internalEventListenerFactory,别名:[],bean对象:org.springframework.context.event.DefaultEventListenerFactory@6580cfdd
bean名称:configBean,别名:[],bean对象:cn.spring.test.config.configBean@7e0b85f9
bean名称:serviceA,别名:[],bean对象:cn.spring.test.service.ServiceA@63355449
bean名称:serviceB1,别名:[],bean对象:ServiceB{serviceA=cn.spring.test.service.ServiceA@9353778}
bean名称:serviceB2,别名:[],bean对象:ServiceB{serviceA=cn.spring.test.service.ServiceA@6a28ffa4}

从输出结果来看,这个结果很明显,加了@Configuration注解的输出中,ServiceA()方法只被调用了一次,而没加@Configuration注解的输出ServiceA方法就被调用了多次,因此@Configuration确保了Bean的单例

总结

区别在于:

  • 带有@Configuration的类中的@Bean方法:这些方法会自动被Spring容器当作Bean工厂来处理,它们之间的依赖关系可以自动解决,即一个@Bean方法可以调用另一个@Bean方法来注入依赖。
  • 非@Configuration类中的@Bean方法:虽然也可以定义Bean,但在默认情况下,这类Bean的管理和发现不如前者直观和自动化,通常需要额外的配置才能被Spring容器正确识别和管理。

代码部分参考了马士兵老师的课件

  • 11
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值