Background knowledge

Two maps

Map

In DefaultListableBeanFactory beanDefinitionMap, statement, this belongs to the source of the IOC container, all registered BeanDefinition is here, a placeholder processor PlaceholderConfigurer is on this Map To deal with.
,>

Map

mergedBeanDefinitions, statement, just as its name implies is stored inside “fusion” BeanDefinition, BeanDefinition derived after the processing
,>

Three kinds of BeanDefinition

RootBeanDefinition GenericBeanDefinition and ChildBeanDefinition are inherited in the AbstractBeanDefinition

  • RootBeanDefinitionTo improve theAbstractBeanDefinitionA lot of information, familiarfinal RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);It’s coming backRootBeanDefinition
  • ChildBeanDefinitionOnly thanAbstractBeanDefinitionMore than aparentNameAnd allChildBeanDefinitionThe constructor of theparentNameYou can see that it is designed for inheritance
  • GenericBeanDefinitionOnly moreparentName, but its constructor need not be specifiedparentNameAre recommended after Spring 2.5GenericBeanDefinitionTo register customizationsBeanDefinition

Special attention is getMergedLocalBeanDefinition () returns are RootBeanDefinition, namely through inheritance, the father and the son after fusion processing become RootBeanDefinition in mergedBeanDefinitions

For inheritance of BeanDefinition, refer to the code snippet

    AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext();
    / / register parentBeanDefinition
    GenericBeanDefinition parentBeanDefinition = new GenericBeanDefinition();
    parentBeanDefinition.setBeanClass(ParentChlidBeanDefinition.class);
    MutablePropertyValues parentPropertyValues = new MutablePropertyValues();
    parentPropertyValues.addPropertyValue("name"."ypq");
    parentBeanDefinition.setPropertyValues(parentPropertyValues);
    applicationContext.registerBeanDefinition("parent", parentBeanDefinition);
    / / register ChildDefinition
    GenericBeanDefinition childBeanDefinition = new GenericBeanDefinition();
    childBeanDefinition.setParentName("parent");
    childBeanDefinition.setBeanClass(ParentChlidBeanDefinition.class);
    MutablePropertyValues childPropertyValues = new MutablePropertyValues();
    childPropertyValues.addPropertyValue("age".10);
    childBeanDefinition.setPropertyValues(childPropertyValues);
    applicationContext.registerBeanDefinition("child", childBeanDefinition);

    applicationContext.refresh();
    ParentChlidBeanDefinition parent = applicationContext.getBean("parent", ParentChlidBeanDefinition.class);
    ParentChlidBeanDefinition child = applicationContext.getBean("child", ParentChlidBeanDefinition.class);

    // The parent BeanDefinition has no age
    Assert.assertEquals("ypq", parent.getName());
    Assert.assertNull(parent.getAge());
    // Child BeanDefinition has age
    Assert.assertEquals("ypq", child.getName());
    Assert.assertEquals(10, child.getAge().intValue());
Copy the code

About this part of the background knowledge can also be reference cloud.tencent.com/developer/a…

applicationContexttherefresh()

Three commonly used Postprocessors

  • BeanPostProcessorIs executed when the Bean is instantiated, such asCommonAnnotationBeanPostProcessorTo be responsible for the@PostConstructand@PreDestroy
  • BeanFactoryPostProcessorIn the refresh() section, for examplePropertySourcesPlaceholderConfigurerHandle BeanFactory’s PropertySources
  • BeanDefinitionRegistryPostProcessor, inheritanceBeanFactoryPostProcessor, has a higher priority and dynamically introduces more Beandefinitions

The order and relationship of the three in Refresh () is shown belowWe know that unexplained problems can arise if Spring is not used properly, such as early Bean instantiation being a common cause. To summarize this kind of problem, there are several scenarios

Question one comes earlyBeanPostProcessorInstantiation phase

@Configuration
public class EarlyConfiguration {
    @Bean
    public BeanPostProcessor myBeanPostProcessor(a) {
        return newMyBeanPostProcessor(); }}Copy the code

Error: EarlyConfiguration$$EnhancerBySpringCGLIB$$f48a5B2 is not eligible for getting Processed by all BeanPostProcessors

Analysis: The log indicates that EarlyConfiguration is not processed by all BeanPostProcessors. In order to instantiate MyBeanPostProcessor, Spring must first instantiate EarlyConfiguration. At least one BeanPostProcessor is not instantiated, let alone processed by BeanPostProcessor.

Solution :public static BeanPostProcessor MyBeanPostProcessor (), plus static myBeanPostProcessor() does not rely on EarlyConfiguration and does not cause EarlyConfiguration to be instantiated prematurely

Problem two: early arrivalBeanFactoryPostProcessorphase

@Configuration
public class EarlyConfiguration {
    @PostConstruct
    public void init(a) {
        System.out.println("EarlyConfiguration init...");
    }
    @Bean
    public BeanFactoryPostProcessor myBeanFactoryPostProcessor(a) {
        return newMyBeanFactoryPostProcessor(); }}Copy the code

@Bean method EarlyConfiguration.myBeanFactoryPostProcessor is non-static and returns an object assignable to Spring’s BeanFactoryPostProcessor interface.This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method’s declaring @Configuration class.Add the ‘static’ modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details.

Symptom: EarlyConfiguration Init is not displayed. @postconstruct is not executed

Analysis: the Spring to instantiate MyBeanFactoryPostProcessor, causing EarlyConfiguration instantiation (phase in figure in blue) in advance, the BeanPostProcessor (such as CommonAnnotationBeanP OstProcessor has not yet been instantiated, so his @postConstruct will not be executed

Solution: public static spring BeanFactoryPostProcessor myStaticBeanFactoryPostProcessor (), the static keyword to avoid the dependencies.

Problem three: Arrive earlyBeanDefinitionRegistryPostProcessorphase

@Configuration
public class EarlyConfiguration {
    @Bean
    public BeanDefinitionRegistryPostProcessor myBeanDefinitionRegistryPostProcessor(a) {
        return newMyBeanDefinitionRegistryPostProcessor(); }}Copy the code

Cannot enhance @Configuration bean definition ‘earlyConfiguration’ 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’.

Symptom: If the above logs are printed, EarlyConfiguration is Full mode but not enhanced (i.e. EarlyConfiguration$$EnhancerBySpringCGLIB$$F48a5B2), just a Lite mode (normal) Configuration

Analysis: the Spring to instantiate MyBeanDefinitionRegistryPostProcessor, Cause EarlyConfiguration instantiation (phase in the purple picture) in advance, and ConfigurationClassPostProcessor# postProcessBeanFactory (namely enhanceConfigurationC enhance implementation method Lasses ()) after BeanDefinitionRegistryPostProcessor instantiation and spring BeanFactoryPostProcessor instantiated before execution, as have generated the Lite mode EarlyConfiguration instance, It can’t be enhanced.

Solution: public static BeanDefinitionRegistryPostProcessor myStaticBeanDefinitionRegistryPostProcessor (), the static keyword to avoid the dependencies.

Problem fourgetObjectTypeThe returns nullFactoryBean

The getObjectType of a FactoryBean depends on @value (“${car-factory.brand}”) to determine the type

@Configuration
public class EarlyConfiguration {
    @Bean
    public BeanFactoryPostProcessor carBeanFactoryPostProcessor(ConfigurableEnvironment configurableEnvironment) {
        return new MyBeanFactoryPostProcessor();
    }
    @Bean
    public static FactoryBean carFactory(a) {
        return newCarFactory(); }}public class CarFactory implements FactoryBean.InitializingBean {
    @Value("${car-factory.brand}")
    private String brand;
    // ...
    @Override
    publicClass<? > getObjectType() {if (brand == null) {
            return null;
        }
        try {
            clazz = this.getClass().getClassLoader().loadClass(brand);
        } catch (ClassNotFoundException e) {
            // ignore
        }
        return clazz;
    }
    @Override
    public void afterPropertiesSet(a) throws Exception {
        LOGGER.error("the brand is {}", brand); }}Copy the code

Symptom: “The brand is null” is printed, which is a special case of Problem 2

Analysis: First let’s take a lookgetBeanThe process ofBeanDefinitionChange tomethodValidationPostProcessorFor example, it takes a construct argument, calledgetBeanNamesForTypeGets the BeanName that matches this parameter type, ifFactoryBean#getObjectTypeThe type cannot be determined and Spring will fully initialize itFactoryBeanTry to determine the type.In the figuregetMergedLocalBeanDefinitionLogic: check firstmergedBeanDefinitionsIf it does, it will return it directly. If it does not, it will generate a “fused” BeanDefinition from the parent relationship of the passed BeanDefinitionmergedBeanDefinitionsIt’s kind of like caching.

Back in our case, propertySourcesPlaceholderConfigurer is used to replace the placeholder spring BeanFactoryPostProcessor, belong to PriorityOrderd, its postProcessBeanFactory will Modify BeanDefinitionMap so that placeholders are replaced with actual values. Usually mergedBeanDefinitions has a BeanDefinitionMap cached at this point, so this change will not take effect immediately. A call to clearMetadataCache to clean up mergedBeanDefinitions must take effect after the BeanFactoryPostProcessor instantiation and invocation phases (shown in blue).

But the custom carBeanFactoryPostProcessor need structure parameters, Cause carFactory instantiation together when myBeanFactoryPostProcessor instantiation, earlier than clearMetadataCache, therefore carFactory print null

conclusion

In general, Spring’s initialization in advance can be divided into three categories, respectively is BeanPostProcessor (green), Spring BeanFactoryPostProcessor (blue), BeanDefinitionRegistryPostProcessor three (purple) Phases can be preinitialized, especially if the FactoryBean’s getObjectType returns NULL. Master the following two pothole avoidance guidelines:

  • PostProcessorUse the static keyword
  • FactoryBeanIdentify Bean types early