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

时间:2025-02-17 20:56:18

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

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

请看下面一段代码:

@Configuration
public class MyTestConfig {

    @Bean
    public Driver driver(){
        Driver driver = new Driver();
        (1);
        ("driver");
        (car());
        return driver;
    }

    @Bean
    public Car car(){
        Car car = new Car();
        (1);
        ("car");
        return car;
    }
}

测试代码如下

@RunWith()
@SpringBootTest
public class TestApplicationTests {

    @Autowired
    private Car car;

    @Autowired
    private Driver driver;

    @Test
    public void contextLoads() {
        boolean result = () == car;
        (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 : ()) {
            BeanDefinition beanDef = (beanName);
            if ((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 (() && (beanName)) {
                    ("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'.");
                }
                (beanName, (AbstractBeanDefinition) beanDef);
            }
        }
        if (()) {
            // nothing to enhance -> return immediately
            return;
        }
        ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
        for (<String, AbstractBeanDefinition> entry : ()) {
            AbstractBeanDefinition beanDef = ();
            // If a @Configuration class gets proxied, always proxy the target class
            (AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, );
            try {
                // Set enhanced subclass of the user-specified bean class
                Class<?> configClass = (this.beanClassLoader);
                Class<?> enhancedClass = (configClass, this.beanClassLoader);//生成代理的class
                if (configClass != enhancedClass) {
                    if (()) {
                        (("Replacing bean definition '%s' existing class '%s' with " +
                                "enhanced class '%s'", (), (), ()));
                    }
                    //替换class,将原来的替换为CGLIB代理的class
                    (enhancedClass);
                }
            }
            catch (Throwable ex) {
                throw new IllegalStateException("Cannot load configuration class: " + (), ex);
            }
        }
    }

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

//是否为配置类
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
return (isFullConfigurationCandidate(metadata) || isLiteConfigurationCandidate(metadata));
}

//是否为完整配置类
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
return (());
}
//是否为精简配置类
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
    // Do not consider an interface or an annotation...
    if (()) {
        return false;
    }

    // Any of the typical annotations found?
    for (String indicator : candidateIndicators) {
        if ((indicator)) {
            return true;
        }
    }

    // Finally, let's look for @Bean methods...
    try {
        return (());
    }
    catch (Throwable ex) {
        if (()) {
            ("Failed to introspect @Bean methods on class [" + () + "]: " + ex);
        }
        return false;
    }
}
//精简配置类包含的注解
static {
    (());
    (());
    (());
    (());
}

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

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