随着spingboot的大火,注解式配置受到了大家的热烈欢迎,而@Component和@Configuration都可以作为配置类,之前一直都没觉得这两个用起来有什么差别,可能有时程序跑的和自己想的有所区别也没注意到。

直到看到这篇文章:https://my.oschina.net/guangshan/blog/1807721 。我意识到@Component和@Configuration是有区别的,错误的使用可能会导致严重的后果。

请看下面一段代码:

@Configuration
public class MyTestConfig {@Beanpublic Driver driver(){Driver driver = new Driver();driver.setId(1);driver.setName("driver");driver.setCar(car());return driver;}@Beanpublic Car car(){Car car = new Car();car.setId(1);car.setName("car");return car;}
}测试代码如下@RunWith(SpringRunner.class)
@SpringBootTest
public class TestApplicationTests {@Autowiredprivate Car car;@Autowiredprivate Driver driver;@Testpublic void contextLoads() {boolean result = driver.getCar() == car;System.out.println(result ? "同一个car" : "不同的car");}}打印结果如下:
同一个car

替换为Component后的打印结果:

不同的car

从上面的结果可以发现使用Configuration时在driver和spring容器之中的是同一个对象,而使用Component时是不同的对象。
造成不同结果的原因在ConfigurationClassPostProcessor类之中,通过调用enhanceConfigurationClasses方法,为被注解@Configuration的类进行CGLIB代理,代码如下:

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {Map<String, AbstractBeanDefinition> configBeanDefs = new LinkedHashMap<String, AbstractBeanDefinition>();for (String beanName : beanFactory.getBeanDefinitionNames()) {BeanDefinition beanDef = beanFactory.getBeanDefinition(beanName);if (ConfigurationClassUtils.isFullConfigurationClass(beanDef)) {//判断是否被@Configuration标注if (!(beanDef instanceof AbstractBeanDefinition)) {throw new BeanDefinitionStoreException("Cannot enhance @Configuration bean definition '" +beanName + "' since it is not stored in an AbstractBeanDefinition subclass");}else if (logger.isWarnEnabled() && beanFactory.containsSingleton(beanName)) {logger.warn("Cannot enhance @Configuration bean definition '" + beanName +"' since its singleton instance has been created too early. The typical cause " +"is a non-static @Bean method with a BeanDefinitionRegistryPostProcessor " +"return type: Consider declaring such methods as 'static'.");}configBeanDefs.put(beanName, (AbstractBeanDefinition) beanDef);}}if (configBeanDefs.isEmpty()) {// nothing to enhance -> return immediatelyreturn;}ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {AbstractBeanDefinition beanDef = entry.getValue();// If a @Configuration class gets proxied, always proxy the target classbeanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);try {// Set enhanced subclass of the user-specified bean classClass<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);//生成代理的classif (configClass != enhancedClass) {if (logger.isDebugEnabled()) {logger.debug(String.format("Replacing bean definition '%s' existing class '%s' with " +"enhanced class '%s'", entry.getKey(), configClass.getName(), enhancedClass.getName()));}//替换class,将原来的替换为CGLIB代理的classbeanDef.setBeanClass(enhancedClass);}}catch (Throwable ex) {throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);}}}

判断是否为配置类的代码如下:

//是否为配置类
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {return (isFullConfigurationCandidate(metadata) || isLiteConfigurationCandidate(metadata));
}//是否为完整配置类
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {return metadata.isAnnotated(Configuration.class.getName());
}
//是否为精简配置类
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {// Do not consider an interface or an annotation...if (metadata.isInterface()) {return false;}// Any of the typical annotations found?for (String indicator : candidateIndicators) {if (metadata.isAnnotated(indicator)) {return true;}}// Finally, let's look for @Bean methods...try {return metadata.hasAnnotatedMethods(Bean.class.getName());}catch (Throwable ex) {if (logger.isDebugEnabled()) {logger.debug("Failed to introspect @Bean methods on class [" + metadata.getClassName() + "]: " + ex);}return false;}
}
//精简配置类包含的注解
static {candidateIndicators.add(Component.class.getName());candidateIndicators.add(ComponentScan.class.getName());candidateIndicators.add(Import.class.getName());candidateIndicators.add(ImportResource.class.getName());
}

从上面可以看到,虽然Component注解也会当做配置类,但是并不会为其生成CGLIB代理Class,所以在生成Driver对象时和生成Car对象时调用car()方法执行了两次new操作,所以是不同的对象。当时Configuration注解时,生成当前对象的子类Class,并对方法拦截,第二次调用car()方法时直接从BeanFactory之中获取对象,所以得到的是同一个对象。

至于产生CGLIB代理的流程,可以看一下下面链接,其中含有详细介绍:https://my.oschina.net/guangshan/blog/1807721

@Component和@Configuration作为配置类的差别相关推荐

  1. @param注解的用法解析_SpringBoot 配置类解析

    本文首发于 vivo互联网技术 微信公众号 链接:https://mp.weixin.qq.com/s/NvPO5-FWLiOlrsOf4wLaJA 作者:Li Wanghong SpringBoot ...

  2. @Component与@Configuration区别

    @Component与@Configuration区别: @Configuration本质上还是@Component. @Configuration标记的类必须符合下面的要求: 1.配置类不能是 fi ...

  3. Spring - @Import - 导入配置类

    目录 前言 普通类导入 @Configuration的配置类导入 ImportSelector 方式导入 前言 @Import注解用来导入配置类或一些需要前置加载的类,其可以通过快速导入的方式实现把实 ...

  4. 软件框架SpringBoot-实现使用@Component@Data@Configuration@Bean(配置类控制类实体类)等方法实现将配置文件从8080端口显示在网页上

    一.前言 1.该程序代码是使用idea2021.12版本编写的,若使用其他软件请对照好配置: 2.这个程序具体的内容我忘了,只知道使用@Component@ConfigurationPropertie ...

  5. Spring的@Configuration配置类-Full和Lite模式

    前言 各位小伙伴大家好,我是A哥.这是一篇"插队"进来的文章,源于我公众号下面的这句评论: 官方管这两种模式分别叫:Full @Configuration和lite @Bean m ...

  6. 配置类Configuration

    配置类@Configuration 01.概述 配置类:在springboot中被@Configuration或者@SpringBootConfiguration标注的类称之为配置类. 02.作用&a ...

  7. 4、mybatis通过配置类Configuration 实现初始化

    对于初学者,如果进行mybatis的学习呢?我总结了几点,会慢慢的更新出来.首先大家需要了解mybatis是什么.用mybatis来做什么.为什么要用mybatis.有什么优缺点:当知道了为什么的时候 ...

  8. Springboot 配置类( @Configuration) 不能使用@Value注解从application.propertyes中加载值以及Environment为null解决方案

    Springboot 配置类( @Configuration) 不能使用@Value注解从application.propertyes中加载值以及Environment为null解决方案 参考文章: ...

  9. 2字段添加注释_2w字长文给你讲透了配置类为什么要添加 @Configuration注解

    文章来源:https://mp.weixin.qq.com/s/5UvbeEnZBS7niAJw_f-6pQ 原文作者:程序员DMZ Spring 用的爽不爽?在你爽的同时,你也知道为什么这么爽,在 ...

最新文章

  1. linux添加py自启动脚本_PyInstaller详解:将.py文件打包成exe文件
  2. python【蓝桥杯vip练习题库】ADV-147学霸的迷宫(广搜 bfs经典问题)
  3. php大于等于符号怎么打出来_PHP常用的特殊运算符号(连续小于符号,三个小于符号,eot,eod,echo示例,print示例)...
  4. 树莓派3 有线静态路由设置_clash在树莓派
  5. 开源的SIP协议栈 PJSIP
  6. HDC.2019后再发力,AppGallery Connect服务新升级
  7. LinuxQuestions.org庆祝16岁生日
  8. 计算机专业评副高论文要求,护士晋升副高职称论文要求
  9. kij是不是c语言语句,C语言考试题库及答案分析(总35页).doc
  10. 华为路由器ws5200虚拟服务器,想处理垃圾路由吗?那就先入手这款华为WS5200路由器吧...
  11. 寻虫记:BOM头制造的冤案,无故多出空白行
  12. 写了三年程序,我25了.
  13. 创新案例分享 | 升级改造干部档案管理系统,精确剖析干部执行力情况
  14. 【Social listening实操】从社交媒体传播和文本挖掘角度解读《欢乐颂2》
  15. unity显示no camera rendering
  16. 相机标定实验过程注意问题及总结
  17. ♥ 七夕七夕 ♥ - 怎么给女朋友送礼物攻略
  18. 利用腾讯云函数做蓝奏云解析API
  19. Win10 或者Win7 64位系统安装32位的CAD2010教程
  20. ACR2010_现实医疗环境下RA缓解率低是否可以用预测因素解释

热门文章

  1. [转载] 面试题:说说Java中接口、类、成员变量、成员方法、构造方法有哪些访问修饰符和他们的作用范围
  2. 软件测试工程师阶段_软件工程测试阶段
  3. java jpa_Java JPA 语法知识
  4. springboot公共模块打包_解决SpringBoot多模块发布时99%的问题?
  5. dbms_DBMS | 并发控制
  6. 如何设计不宕机的 Redis 高可用服务?
  7. 三层业务类(DAL)必用的通用方法之一
  8. 操作系统(王道笔记第三章内存)
  9. Python3自带HTTP文件传输服务(局域网文件共享)
  10. 用委托来实现IEqualityComparer接口