On the loading priority of SpringBoot Bean

  • 2021-12-12 08:31:13
  • OfStack

The problem of directory Bean loading priority is the same as the loading sequence in the same class @ DependsOn control sequence @ Order can't control sequence Spring control Bean loading sequence use Spring @ Order control bean loading sequence use Spring @ DependsOn control bean loading sequence Summary 1

Bean Load Priority Issues

The order in which the spring container loads bean is indeterminate, and the spring framework does not agree on a specific sequential logic specification. But spring guarantees that B will be loaded before A if A depends on B (such as the @ Autowired B variable in beanA).

Load order in the same class

Constructor > > @Autowired > > @ PostConstruct > > @Bean

@ DependsOn control sequence

If A does not depend on B, but A needs to be initialized after B, you can use @ DependsOn (value = "Bbeanname"). Name needs to be manually specified on @ Bean of B, otherwise it cannot be found.

@ Order can't control sequence

The @ Order annotation does not change the Bean loading priority. The @ Order annotation sets the order in which Bean is loaded into list


@Order(2)
@Component
public class AnoBean1 implements IBean {
    private String name = "ano order bean 1";
    public AnoBean1() {
        System.out.println(name);
    }
}
@Order(1)
@Component
public class AnoBean2 implements IBean {
    private String name = "ano order bean 2";
    public AnoBean2() {
        System.out.println(name);
    }
}
@Component
public class AnoTestBean {
    public AnoTestBean(List<IBean> anoBeanList) {
        for (IBean bean : anoBeanList) {
            System.out.println("in ano testBean: " + bean.getClass().getName());
        }
    }
}

The above code outputs the result

ano order bean 1
ano order bean 2
in ano testBean: AnoBean2
in ano testBean: AnoBean1

Spring controls the loading sequence of Bean

Using Spring @ Order to control the loading sequence of bean

Two demos bean


package com.ziyear.spring4_2.order;
public class Demo1Service {
}

package com.ziyear.spring4_2.order;
public class Demo2Service {
}

Two configuration classes, notice the order in which the @ Order configuration is loaded


package com.ziyear.spring4_2.order;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@Configuration
@Order(2)
public class Demo1Config {
    @Bean
    public Demo1Service demo1Service(){
        System.out.println("demo1config  Loaded ");
        return new Demo1Service();
    }
}

package com.ziyear.spring4_2.order;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.annotation.Order;
@Configuration
@Order(1)
public class Demo2Config {
    @Bean
    public Demo2Service demo2Service(){
        System.out.println("demo2config  Loaded ");
        return new Demo2Service();
    }
}

Run


package com.ziyear.spring4_2.order;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context =
                new AnnotationConfigApplicationContext("com.ziyear.spring4_2.order");
    }
}

Output result

demo2config loaded
demo1config loaded

Use Spring @ DependsOn to control bean loading sequence

The order in which the spring container loads the bean is indeterminate, and the spring framework does not agree on a specific sequential logic specification. But spring guarantees that B will be loaded before A if A depends on B (such as the @ Autowired B variable in beanA). But if beanA does not depend directly on B, how do we get B to load first?

Control bean initialization sequence

There may be scenarios where bean A indirectly depends on bean B. For example, Bean B should need to update 1 global cache, which may be implemented in singleton mode and not registered in spring container, and bean A needs to use this cache; Therefore, if bean B is not ready, bean A cannot be accessed.

In another scenario, bean A is the event publisher (or JMS publisher), and bean B (or more) is responsible for listening for these events, typically in observer mode. We don't want B to miss any events, so B needs to be initialized first.

In short, there are many scenarios where bean B should be initialized before bean A to avoid all kinds of negative effects. We can use the @ DependsOn annotation on bean A to tell the container that bean B should be initialized first. The following is illustrated by examples.

Example description

The examples are illustrated through the event mechanism, Person and Man, and then run through the spring configuration. The example is simplified for ease of illustration.

Class Person


public class Person {
    public static void say(){
        System.out.println("person.say():Im a person");
    }
}

Class Man


public class Man {
    public void say(){
        System.out.println("man.say():Im a man:");
    }
}

AppConfig.java

Configure the run class.


@Configuration
@ComponentScan("com.ziyear.demo")
public class Appconfig {
    @Bean(initMethod = "say")
    @DependsOn("man")
    public Person personBean () {
        return new Person();
    }
    @Bean(name = "man", initMethod = "say")
    //@Lazy
    public Man manBean () {
        return new Man();
    }
    public static void main (String[] strings) {
        new AnnotationConfigApplicationContext(Appconfig.class);
    }
}

Run the main method of AppConfig, and the output is:

man.say():Im a man:
person.say():Im a person

Summary 1

If we comment out @ DependsOn ("man"), we may not be sure we get the same result. Try to run the main method several times, and occasionally see different say methods being executed separately. Why occasionally? Because during container startup, spring loads bean in any order.

So can you make it 100% certain when you don't use @ DependsOn? You can use the @ Lazy annotation on manBean (). Because the Man is not loaded at startup, it is loaded when other bean needs it. This time only Person is initialized.


person.say():Im a person

Now add @ DependsOn again, and do not delete the @ Lazy annotation. The output results and the first time 1. Although we used the @ Lazy annotation, Man is still loaded at startup because @ DependsOn indicates that Man is needed.


Related articles: