The difference between @Component and @Configuration as configuration classes

This article introduces the differences between @Component and @Configuration as configuration classes, including @Component and @Configuration as examples of different use of configuration classes, application techniques, summary of basic knowledge points and points for attention, which have some reference value and can be referred to by friends who need it.
 

With the spingboot fire, annotated configurations were welcomed, and both @Component and @Configuration could be used as configuration classes, which had never been seen before. Sometimes programs run differently from what they thought.

Until you see this article: https://my.oschina.net/guangshan/blog/1807721.I realize that @Component is different from @Configuration and that incorrect use can have serious consequences.

Look at the following code:
 

@Configuration
public class MyTestConfig {

    @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;
    }
}

//The test code is as follows

@RunWith(SpringRunner.class)
@SpringBootTest
public class TestApplicationTests {

    @Autowired
    private Car car;

    @Autowired
    private Driver driver;

    @Test
    public void contextLoads() {
        boolean result = driver.getCar() == car;
        System.out.println(result ? "Same car" : "Different car");
    }

}

//The printout is as follows:
//Same car

Replace with the printout of Component:

Different car

From the results above, you can see that Configuration is used with the same object in the driver and spring containers, while Component is used with different objects.(
The reason for the different results is that in the ConfigurationClassPostProcessor class, a CGLIB proxy is made for the annotated @Configuration class by calling the enhanceConfigurationClasses method, with the following code:
 

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)) {//Determine if labeled by @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 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);//Generate proxy's 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()));
                    }
                    //Replace class, replace original with class of CGLIB proxy
                    beanDef.setBeanClass(enhancedClass);
                }
            }
            catch (Throwable ex) {
                throw new IllegalStateException("Cannot load configuration class: " + beanDef.getBeanClassName(), ex);
            }
        }
    }

The code to determine whether it is a configuration class is as follows:

//Is it a configuration class
public static boolean isConfigurationCandidate(AnnotationMetadata metadata) {
return (isFullConfigurationCandidate(metadata) || isLiteConfigurationCandidate(metadata));
}

//Is Full Configuration Class
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
return metadata.isAnnotated(Configuration.class.getName());
}
//Whether to configure classes for streamlining
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;
    }
}
//Streamline the annotations contained in the configuration class
static {
    candidateIndicators.add(Component.class.getName());
    candidateIndicators.add(ComponentScan.class.getName());
    candidateIndicators.add(Import.class.getName());
    candidateIndicators.add(ImportResource.class.getName());
}

As you can see from the above, although the Component annotation also acts as a configuration class, it does not generate a CGLIB proxy class for it, so the car() method is called twice when the Driver object is generated and when the Car object is generated, so it is a different object.When Configuration commented, it generated a subclass Class of the current object, intercepted the method, and got the object directly from BeanFactory the second time the car() method was called, so it was the same object.

For the process of generating the CGLIB proxy, see the links below, which contain detailed descriptions: https://my.oschina.net/guangshan/blog/1807721

 

-------------------- 
Author: Transfer from Version 1
Source: CSDN
Original: https://blog.csdn.net/long476964/article/details/80626930
Copyright Statement: This is an original article of the blogger. Please attach a link to the blog post to reproduce it!

Tags: Programming Spring

Posted on Tue, 24 Mar 2020 09:24:29 -0700 by pacome