Spring @Configuration 和 @Component 区别
Spring @Configuration 和 @Component 区别
一句话概括就是
@Configuration
中所有带@Bean
注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。
@Configuration 注解:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
String value() default "";
}
从定义来看, @Configuration 注解本质上还是 @Component,因此 <context:component-scan/> 或者 @ComponentScan 都能处理@Configuration 注解的类。
@Configuration 标记的类必须符合下面的要求:
配置类必须以类的形式提供(不能是工厂方法返回的实例),允许通过生成子类在运行时增强(cglib 动态代理)。
配置类不能是 final 类(没法动态代理)。
配置注解通常为了通过 @Bean 注解生成 Spring 容器管理的类,
配置类必须是非本地的(即不能在方法中声明,不能是 private)。
任何嵌套配置类都必须声明为static。
@Bean 方法可能不会反过来创建进一步的配置类(也就是返回的 bean 如果带有 @Configuration,也不会被特殊处理,只会作为普通的 bean)。
@Component 注意
@Component
注解并没有通过 cglib 来代理@Bean
方法的调用,因此像下面这样配置时,就是两个不同的 country。@Component
public class MyBeanConfig {
@Bean
public Country country(){
return new Country();
}
@Bean
public UserInfo userInfo(){
return new UserInfo(country());
}
}有些特殊情况下,我们不希望
MyBeanConfig
被代理(代理后会变成WebMvcConfig$$EnhancerBySpringCGLIB$$8bef3235293
)时,就得用@Component
,这种情况下,上面的写法就需要改成下面这样:@Component
public class MyBeanConfig {
@Autowired
private Country country;
@Bean
public Country country(){
return new Country();
}
@Bean
public UserInfo userInfo(){
return new UserInfo(country);
}
}
加载过程
Spring 容器在启动时,会加载默认的一些 PostPRocessor,其中就有 ConfigurationClassPostProcessor,这个后置处理程序专门处理带有 @Configuration 注解的类,这个程序会在 bean 定义加载完成后,在 bean 初始化前进行处理。主要处理的过程就是使用 cglib 动态代理增强类,而且是对其中带有 @Bean 注解的方法进行处理。
,查找到所有带有@Configuration
注解的 bean 定义,然后在第二个 for 循环中,通过下面的方法对类进行增强:使用增强后的类替换了原有的
beanClass
:所以到此时,所有带有@Configuration
注解的 bean 都已经变成了增强的类。也就是当方法有@Bean
注解的时候,就会执行这个回调方法。
@Bean
注解方法执行策略先给一个简单的示例代码:@Configuration
public class MyBeanConfig {
@Bean
public Country country(){
return new Country();
}
@Bean
public UserInfo userInfo(){
return new UserInfo(country());
}
}相信大多数人第一次看到上面 userInfo() 中调用 country() 时,会认为这里的 Country 和上面 @Bean 方法返回的 Country 可能不是同一个对象,因此可能会通过下面的方式来替代这种方式:
@Autowired
private Country country;
实际上不需要这么做(后面会给出需要这样做的场景),直接调用 country() 方法返回的是同一个实例。
pring中,对于配置类来讲,其实是有分类的,大体可以分为两类,一类称为LITE模式,另一类称为FULL模式,那么对应上面的注解,@Component就是LITE类型,@Configuration就是FULL类型,如何理解这两种配置类型呢?
当我们使用
@Component
实现配置类时:从结果可知,
foo()
方法执行了两次,一次是bean方法执行的,一次是eoo()
调用执行的,所以两次生成的foo
对象是不一样的。很符合大家的预期,但是当我们使用
@Configuration
标注配置类时,执行结果如下:这里可以看到foo()
方法只执行了一次,同时eoo()
方法调用foo()
生成的foo对象是同一个。
这也就是
@Component
和@Configuration
的区别现象展示,那么为什么会有这样的一个现象?
我们来考虑一个问题,就是eoo()方法中调用了foo()方法,很明显这个foo()这个方法就是会形成一个新对象
,假设我们调用的foo()方法不是原来的foo()方法,
是不是就可能不会形成新对象?
如果我们在调用foo()方法的时候去容器中获取一下foo这个Bean,是不是就可以达到这样的效果?
那如何才能达到这样的效果呢?有一个方法,代理!
换句话说,我们调用的eoo()和foo()方法,包括AppConfig都被Spring代理了
,那么这里我们明白了@Component与@Configuration最根本的区别,
那就是@Configuration标注的类会被Spring代理,
其实这样描述不是非常严谨,更加准确的来说应该是
3.Spring如何实现FULL配置的代理
如果要明白这一点,那么还需要明确一个前提,就是Spring在什么时间将这些配置类转变成FULL模式或者LITE模式的,接下来我们就要介绍个人认为在Spring中非常重要的一个类,现在需要知道的是ConfigurationClassPostProcessor这个类是在什么时间被实例化的?
3.2ConfigurationClassPostProcessor在什么时间被实例化
要回答这个问题,需要先明确一个前提,那就是ConfigurationClassPostProcessor这个类对应的BeanDefinition在什么时间注册到Spring的容器中的,因为Spring的实例化比较特殊,主要是基于BeanDefinition来处理的这个类是在什么时间被注册为一个Beandefinition的?这个可以在源代码中找到答案,具体其实就是在初始化这个Spring容器的时候。。
如果一个类的BeanDefinition的Attribute中有Full配置属性,那么这个类就会被Spring代理
我们讲了Spring是通过对BeanDefinition进行解析,处理,实例化,填充,初始化以及众多回调等等步骤才会形成一个Bean,那么现在ConfigurationClassPostProcessor既然已经形成了一个BeanDefinition。
这两个方法应该就是ConfigurationClassPostProcessor最为关键的了,我们在这里先简单的总结一下,第一个方法主要完成了内部类,@Component,@ComponentScan,@Bean,@Configuration,@Import等等注解的处理,然后生成对应的BeanDefinition,另一个方法就是对@Configuration使用CGLIB进行增强,那我们先来看Spring是在哪里区分配置的LITE模式和FULL模式?
想想看,BeanDefinition主要是做什么的?这个主要是用来描述Class对象的,例如这个Class是不是抽象的,作用域是什么,是不是懒加载等等信息,那如果一个Class对象有一个“属性”是BeanDefinition描述不了的,那这个要如何处理呢?那这个接口AttributeAccessor又派上用场了,你可以向其中存放任何你定义的数据,可以理解为一个map,现在了解BeanDefinition的属性的含义了么?
在这里也能看到
@Configuration(proxyBeanMethods = false)
和@Component
一样效果,都是LITE模式@Component在Spring中是代表LITE模式的配置注解,这种模式下的注解不会被Spring所代理,就是一个标准类,如果在这个类中有@Bean标注的方法,那么方法间的相互调用,其实就是普通Java类的方法的调用。
@Configuration在Spring中是代表FULL模式的配置注解,这种模式下的类会被Spring所代理,那么在这个类中的@Bean方法的相互调用,就相当于调用了代理方法,那么在代理方法中会判断,是否调用getBean方法还是invokeSuper方法,这里就是这两个注解的最根本的区别。
一句话概括就是@Configuration
中所有带@Bean
注解的方法都会被动态代理,因此调用该方法返回的都是同一个实例。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)