@Component和@Configuration作为配置类的差别

编程

本文章向大家介绍@Component和@Configuration作为配置类的差别,主要包括@Component和@Configuration作为配置类的差别使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

 

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

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

请看下面一段代码:
 

@Configuration

publicclassMyTestConfig{

@Bean

public Driver driver(){

Driver driver = new Driver();

driver.setId(1);

driver.setName("driver");

driver.setCar(car());

return driver;

}

@Bean

public Car car(){

Car car = new Car();

car.setId(1);

car.setName("car");

return car;

}

}

测试代码如下

@RunWith(SpringRunner.class)

@SpringBootTest

publicclassTestApplicationTests{

@Autowired

private Car car;

@Autowired

private Driver driver;

@Test

publicvoidcontextLoads(){

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)) {

thrownew BeanDefinitionStoreException("Cannot enhance @Configuration bean definition "" +

beanName + "" since it is not stored in an AbstractBeanDefinition subclass");

}

elseif (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 immediately

return;

}

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 class

beanDef.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);

try {

// Set enhanced subclass of the user-specified bean class

Class<?> configClass = beanDef.resolveBeanClass(this.beanClassLoader);

Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);//生成代理的class

if (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代理的class

beanDef.setBeanClass(enhancedClass);

}

}

catch (Throwable ex) {

thrownew IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);

}

}

}

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

//是否为配置类

publicstatic boolean isConfigurationCandidate(AnnotationMetadata metadata){

return (isFullConfigurationCandidate(metadata) || isLiteConfigurationCandidate(metadata));

}

//是否为完整配置类

publicstatic boolean isFullConfigurationCandidate(AnnotationMetadata metadata){

return metadata.isAnnotated(Configuration.class.getName());

}

//是否为精简配置类

publicstatic boolean isLiteConfigurationCandidate(AnnotationMetadata metadata){

// Do not consider an interface or an annotation...

if (metadata.isInterface()) {

returnfalse;

}

// Any of the typical annotations found?

for (String indicator : candidateIndicators) {

if (metadata.isAnnotated(indicator)) {

returntrue;

}

}

// 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);

}

returnfalse;

}

}

//精简配置类包含的注解

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

 

-------------------- 
作者:一号版转手 
来源:CSDN 
原文:https://blog.csdn.net/long476964/article/details/80626930 
版权声明:本文为博主原创文章,转载请附上博文链接!

以上是 @Component和@Configuration作为配置类的差别 的全部内容, 来源链接: utcz.com/z/514727.html

回到顶部