Sequence issues in Spring (don't hesitate is the sequence issue you want to know)

Some ordering issues in Spring

The order issue in Spring is also a very important topic, such as how multiple BeanFactoryPostProcessors know which one to execute first; why custom beans can override the default auto-assembled beans; how is the order of interceptors in the AOP interceptor chain determined And so on.

I believe that after reading this document, you should be able to have some understanding of the order of Spring!

First give the conclusion directly as follows:

1. If there are the following special interfaces or annotations, use their order

Special interfaces or annotations are: PriorityOrdered interface, Ordered interface, @Priority annotation, @Order annotation

2. If not, use beanDefinitionNamesthe order

So what determines the order of beanDefinitionNames? ? ? explained below

The following are some common examples related to order in Spring

Example 1: Spring registers non-lazy loaded beans

// DefaultListableBeanFactory.class
public void preInstantiateSingletons() throws BeansException {
    
    

    // <1> 收集beanDefinitionNames集合
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

    for (String beanName : beanNames) {
    
    
        // <2> 依次注册 Bean
        getBean(beanName);

    }
}
  • <1>At the place, collect the BeanDefinition collection
  • <2>, the order of instantiation is generally consistent with the order of registration, but when processing property dependencies , constructor parameter dependencies , and other dependencies during the instantiation process, the dependent Bean will be instantiated first!

Example 2: Spring processing BeanFactoryPostProcessorinterface

// PostProcessorRegistrationDelegate.class
public static void invokeBeanFactoryPostProcessors(
    ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    
    

    // <1> 收集BeanDefinitionRegistryPostProcessor集合
    String[] postProcessorNames =
        beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
    for (String ppName : postProcessorNames) {
    
    
        // <2> 优先处理 PriorityOrdered 接口
        if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
    
    
            processedBeans.add(ppName);
        }
    }

    // <3> 再处理 Ordered 接口的
    for (String ppName : postProcessorNames) {
    
    
        if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
    
    
        }
    }

    // <4> 最后处理不是这 2 个接口的

}
  • <1>At the place, the BeanDefinitionRegistryPostProcessor collection is collected, but it is collected according to the order of registration (ie beanDefinitionNames)
  • <2>, the PriorityOrdered interface is prioritized
  • <3>, and then process the Ordered interface
  • <4>, the final processing is not for these two interfaces

Example 3: AnnotationAwareOrderComparator annotation sort comparison

Function: The function of this comparator is to sort the content marked with annotations, @Priority takes precedence over @Order, and the same annotation depends on the size of the value

This annotation comparator is used almost everywhere in Spring where beans need to be sorted, such as:

  • When instantiating the ApplicationContextInitializer interface, the code and comments are as follows:
// 代码位置:AbstractContextLoader.class

private void invokeApplicationContextInitializers(ConfigurableApplicationContext context,
                                                  MergedContextConfiguration mergedConfig) {
    
    
	... ...// <1> 对 initializerInstances 排序
    AnnotationAwareOrderComparator.sort(initializerInstances);
    for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : initializerInstances) {
    
    
        // <2> 比较后挨个实例化
        initializer.initialize(context);
    }
}
  • The loadFactories method loads and instantiates a class of a specified type from the configuration. The code and comments are as follows:
// 代码位置:SpringFactoriesLoader.class

// 功能:加载配置中的 factoryClass 类型的定义
public static <T> List<T> loadFactories(Class<T> factoryClass, @Nullable ClassLoader classLoader) {
    
    

    // <1> 加载factoryClass 类型的列表
    List<String> factoryNames = loadFactoryNames(factoryClass, classLoaderToUse);

    List<T> result = new ArrayList<>(factoryNames.size());
    for (String factoryName : factoryNames) {
    
    
        // <2> 逐个实例化
        result.add(instantiateFactory(factoryName, factoryClass, classLoaderToUse));
    }
    // <3> 排序
    AnnotationAwareOrderComparator.sort(result);
    return result;
}
  • When AOP sorts, the code and comments are as follows:
// 代码位置:AbstractAdvisorAutoProxyCreator.class

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
    
    
    // <1> 查找候选的 Advisor
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // <2> 适配当前 beanClass 的 Advisor
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
    
    
        // <3> 对 Advisor 排序
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

What determines the order of beanDefinitionNames

Need to know the background knowledge

1. Spring startup process, Spring Boot startup process

2. The principle of Spring Boot automatic assembly

3. How does the BeanFactoryPostProcessor post-processor work, especially the key pointsConfigurationClassPostProcessor

Simple analysis process of the registration process

  1. Because the beanDefinitionNames attribute can only be added through the following code (there is and only this add method)
// 代码位置:DefaultListableBeanFactory.class

private volatile List<String> beanDefinitionNames = new ArrayList<>(256);

public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition)
    throws BeanDefinitionStoreException {
    
    

    // 注册
    this.beanDefinitionNames.add(beanName);

}

So the order of this attribute is mainly related to the invocation of this.registry.registerBeanDefinitionthe method

  1. Look at the simple analysis of ConfigurationClassPostProcessor

The code simplification for registering BeanDefinition by calling the container registry is omitted as follows:

// 代码位置:ConfigurationClassPostProcessor.class
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    
    

    do {
    
    
        // <1> 解析 candidates
        parser.parse(candidates);

        // <2> 获取【有序的】配置类集合
        Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());

        // <3> 处理配置类
        this.reader.loadBeanDefinitions(configClasses);
    }
    while (!candidates.isEmpty());
}
  • <1>At the beginning, there is only one class of candidates in general, which is the class @SpringApplicationmarked annotations; and then parseall configuration classes are processed through various recursive calls inside the method
  • <2>, get all the configuration classes parsed in parsethe stage
  • <3>At the place, handle the registration of [ordered configuration class] and register it in beanDefinitionNamesthe attribute

Based on the above simple analysis, the following conclusions are drawn

Conclusion of the Registration Sequence

Take the following code as an example to illustrate the registration sequence of beanDefinitionNames

@SpringBootApplication
@EnableCaching
@EnableTransactionManagement
@MapperScan(basePackages = "cn.iocoder.springboot.lab21.cache.mapper")
public class Application {
    
    
	public static void main(String[] args) {
    
    
        // 先处理 run 方法中传入的"配置类",即 Application 类
		SpringApplication.run(Application.class);
	}
}
  1. Application has the highest priority

    Because generally there is only one candidate class, that is, the startup class Application, which is the entry point for configuration class Configuration registration

  2. The second is the package where the Application class is located

    That is, our custom package scanning location, that is, our custom Bean!

  3. Handles various ImportSelectorimported classes

    Process the @EnableCaching annotation first, then the @EnableTransactionManagement annotation, and then the @MapperScan annotation

  4. And finally the autowired class

    Remark:

    1. The Configuration class sorted in front will be registered first

    2. The order of Configuration can be controlled, such as through @AutoConfigureBefore, @AutoConfigureAfter, @AutoConfigureOrder, etc.

Explain some order-related issues

Because of the above conclusions, we can try to solve some order-related problems

Question 1: When our custom Bean exists, the default Bean will not be loaded, and the default Bean will only be loaded when our custom Bean does not exist

Answer: According to the registration order above, we scan our custom package first, so our custom Bean is registered first; and @ConditionalOnBean and other conditional annotations may be used during automatic assembly, which will cause the condition to be unsatisfied, so it will no longer be registered default bean

Question 2: Does the order of registration have a big impact

Answer:

1. It has a great impact. Except for the classes that mark or implement special annotations or interfaces, other classes will be affected to a certain extent.

2. It is difficult to fully grasp the order of Spring Boot's automatic assembly, which is also an aspect that makes Spring Boot relatively obscure

Leave a question for the judges to answer

Question: Does the order of the following @EnableCaching annotations and @EnableTransactionManagement affect the order of the two interceptors? ? ?

Method 1 :

@SpringBootApplication
@EnableTransactionManagement
@EnableCaching
@MapperScan(basePackages = "cn.iocoder.springboot.lab21.cache.mapper")
public class Application {
    
    
	public static void main(String[] args) {
    
    
		SpringApplication.run(Application.class);
	}
}
image-20230429043546519

Way 2 :

@SpringBootApplication
@EnableCaching
@EnableTransactionManagement
@MapperScan(basePackages = "cn.iocoder.springboot.lab21.cache.mapper")
public class Application {
    
    
	public static void main(String[] args) {
    
    
		SpringApplication.run(Application.class);
	}
}
image-20230429043748409

Answer: Judging from the results of the screenshot, it is influential, why?

illustrate:

1. Because @EnableCaching and @@EnableTransactionManagement both pass the ImportSelector mechanism, the above annotations will be processed first; so the transaction in mode 1 will be registered first, and the cache will be registered later

2. Secondly, although AOP will be sorted by Advisor

// 代码位置:AbstractAdvisorAutoProxyCreator.class

protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
     
     
    // <1> 查找候选的 Advisor
    List<Advisor> candidateAdvisors = findCandidateAdvisors();
    // <2> 适配当前 beanClass 的 Advisor
    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
    extendAdvisors(eligibleAdvisors);
    if (!eligibleAdvisors.isEmpty()) {
     
     
        // <3> 对 Advisor 排序
        eligibleAdvisors = sortAdvisors(eligibleAdvisors);
    }
    return eligibleAdvisors;
}

However, the BeanFactoryTransactionAttributeSourceAdvisor of the transaction and the BeanFactoryCacheOperationSourceAdvisor of the cache have no sequence interface or annotation, so the sorting is invalid, and the order of registration is the order of the Advisor

Portal: nanny Spring5 source code analysis

Welcome to exchange technology and work life with the author

contact author

Guess you like

Origin blog.csdn.net/yuchangyuan5237/article/details/130445792