1. Why do I need to control the loading sequence

Springboot complies with the principle of convention over configuration, which greatly solves the problem of cumbersome configuration. Springing. Factories allows you to auto-assemble a widget.

In a normal business scenario, you probably don’t care much about how a bean is registered into the Spring container. Simply declare the bean to be registered with the container as @Component. Spring automatically scans the bean for initialization and loads it into the Spring context container.

When you need to do a business initialization ahead of time at project startup, or when you’re developing some middleware that needs to be autownalized. You declare your own Configuration class, but you may be dealing with several beans that are interdependent. If unchecked, the dependency error could be reported at this point.

But you have already registered the relevant beans into the Spring context. At this point, you need to control the loading order of beans in SpringBoot by some means.

2. Several myths

Before I start talking about how to control the load order, there are two myths.

In the label@ConfigurationClass, the @Bean written first must be registered first

This doesn’t exist. Spring, in the old DAYS of XML, doesn’t have logic written in front of it that must be loaded first. Because XML is not progressively loaded, it is fully parsed, followed by dependency analysis and registration. In SpringBoot, the process of XML being parsed into spring internal objects is eliminated, but the loading method does not change significantly.

using@OrderThis annotation can control the loading order

Strictly speaking, not all beans can be ordered using the @order annotation. It doesn’t make any sense for you to add the @order notation to a normal method or class.

@order controls which beans are loaded in Order.

{@code @order} defines the sort Order for an annotated component. annotation-based ordering is supported for many kinds of components in Spring, even for collection injection where the order values of the target components are taken into account (either from their target class or from their {@code @Bean} method). While such order values may influence priorities at injection points, please be aware that they do not influence singleton startup order which is an orthogonal concern determined by dependency relationships and {@code @DependsOn} declarations (influencing a runtime-determined dependency graph).

Initially the @Order annotation is used to specify the priority of the aspect; It has been enhanced since 4.0 to support collection injection, specifying the order of beans in a collection, and specifically stating that it has no effect on the order of beans between instances.

At present, the following three points are more commonly used:

  • Controls the loading order of AOP’s classes, that is, by@AspectMark of class
  • controlApplicationListenerImplement the load order of the classes
  • controlCommandLineRunnerImplement the load order of the classes

3. How to control it

3.1 @ DependsOn

The @dependson annotation can be used to control the order in which beans are created. This annotation is used to declare that the current bean is dependent on another bean. The dependent bean is guaranteed by the container to be instantiated before the current bean is instantiated.

Example:

@Configuration
public class BeanOrderConfiguration {

    @Bean
    @DependsOn("beanB")
    public BeanA beanA(a){
        System.out.println("bean A init");
        return new BeanA();
    }

    @Bean
    public BeanB beanB(a){
        System.out.println("bean B init");
        return new BeanB();
    }

    @Bean
    @DependsOn({"beanD"."beanE"})
    public BeanC beanC(a){
        System.out.println("bean C init");
        return new BeanC();
    }

    @Bean
    @DependsOn("beanE")
    public BeanD beanD(a){
        System.out.println("bean D init");
        return new BeanD();
    }

    @Bean
    public BeanE beanE(a){
        System.out.println("bean E init");
        return newBeanE(); }}Copy the code

The loading sequence of the above code beans is:

bean B init
bean A init
bean E init
bean D init
bean C init
Copy the code

Use of @dependson:

  • Marked directly or indirectly with@ComponentAbove the annotated class;
  • Marked directly or indirectly with@BeanAnnotation method above;
  • use@DependsOnAnnotations to the class level are only valid when using Component-scanning, if any@DependsOnThe annotation class is used in XML and the annotation is ignored,<bean depends-on="..." />This will work.

3.2 Parameter Injection

On the @bean annotation method, if you pass in a parameter, SpringBoot will automatically look for a reference of that type in the Spring context for that parameter. Initialize an instance of the class first.

Using this feature, we can also control the loading order of beans.

Example:

@Bean
public BeanA beanA(BeanB demoB){
  System.out.println("bean A init");
  return new BeanA();
}

@Bean
public BeanB beanB(a){
  System.out.println("bean B init");
  return new BeanB();
}
Copy the code

As a result, beanB is initialized before beanA.

Note that SpringBoot looks by type. If there are multiple instances of this type registered in the Spring context, you need to specify it with @qualifier (“Bean name “)

3.3 Leverage extension points in the bean lifecycle

In the Spring architecture, everything from container to Bean instantiation & initialization has a lifecycle and provides many extension points that allow you to logically extend these steps.

The loading order of these extensibility points is controlled by Spring itself, and most of them are beyond intervention. We can take advantage of this and extend Spring’s extension points. Add your own business initialization code at the appropriate extension point. Never achieve sequential control.

Most of the extensibility points in the Spring container have been analyzed in detail in the previous article, “Springboot Boot Extension Point super detailed summary, no longer be not concerned with the interview”.

3.4 @ AutoConfigureOrder

This annotation is used to specify the loading order of configuration files. However, in the actual test, it is found that the following use is not effective:

@Configuration
@AutoConfigureOrder(2)
public class BeanOrderConfiguration1 {
    @Bean
    public BeanA beanA(a){
        System.out.println("bean A init");
        return newBeanA(); }}@Configuration
@AutoConfigureOrder(1)
public class BeanOrderConfiguration2 {
    @Bean
    public BeanB beanB(a){
        System.out.println("bean B init");
        return newBeanB(); }}Copy the code

No matter how many numbers you fill in, it doesn’t change the loading order.

So how does @AutoConfigureOrder work?

After testing, @AutoConfigureOrder can only change the order of external dependencies on @Configuration. How to understand external dependencies.

The packages that can be scanned internally by your project are internal Configuration. Spring imports external Configuration using the spring-specific SPI file spring.Factories

In other words,@AutoConfigureOrderCan changespring.factoriesIn the@ConfigurationThe order.

Specific use mode:

@Configuration
@AutoConfigureOrder(10)
public class BeanOrderConfiguration1 {
    @Bean
    public BeanA beanA(a){
        System.out.println("bean A init");
        return newBeanA(); }}@Configuration
@AutoConfigureOrder(1)
public class BeanOrderConfiguration2 {
    @Bean
    public BeanB beanB(a){
        System.out.println("bean B init");
        return newBeanB(); }}Copy the code

Spring. Factories:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  com.example.demo.BeanOrderConfiguration1,\
  com.example.demo.BeanOrderConfiguration2
Copy the code

4. To summarize

In fact, in my work, I’m sure many of you have encountered complex dependencies in bean loading. It is better to leave this uncertainty to Spring to control the order of dependencies between beans when you read the code.

5. Contact the author

Focus on wechat to get more technical dry goods