An instance of using Spring @ DependsOn to control the loading sequence of bean

  • 2021-11-02 00:56:23
  • OfStack

The order in which the spring container loads bean is uncertain, 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 doesn't directly depend on B, how do we get B to load first?

Control the initialization sequence of bean

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 example is illustrated through the event mechanism, the publisher, and the listener, and then run through the spring configuration. The example is simplified for ease of illustration.

EventManager.java

Event management class, maintain listener list, through singleton method to obtain event manager, can increase listener or publish events.


import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
public class EventManager {
    private final List<Consumer<String>> listeners = new ArrayList<>();
    private EventManager() {
    }
    private static class SingletonHolder {
        private static final EventManager INSTANCE = new EventManager();
    }
    public static EventManager getInstance() {
        return SingletonHolder.INSTANCE;
    }
    public void publish(final String message) {
        listeners.forEach(l -> l.accept(message));
    }
    public void addListener(Consumer<String> eventConsumer) {
        listeners.add(eventConsumer);
    }
}

EventPublisherBean.java

Event publishing class, which publishes events through EventManager class.


import com.logicbig.example.EventManager;
public class EventPublisherBean {
    public void initialize() {
        System.out.println("EventPublisherBean initializing");
        EventManager.getInstance().publish("event published from EventPublisherBean");
    }
}

EventListenerBean.java

Event listener, you can add listeners.


import com.logicbig.example.EventManager;
public class EventListenerBean {
    private void initialize() {
        EventManager.getInstance().
                addListener(s ->
                        System.out.println("event received in EventListenerBean : " + s));
    }
}

AppConfig.java

Configure the run class.


@Configuration
@ComponentScan("com.logicbig.example")
public class AppConfig {
    @Bean(initMethod = "initialize")
    @DependsOn("eventListener")
    public EventPublisherBean eventPublisherBean () {
        return new EventPublisherBean();
    }
    @Bean(name = "eventListener", initMethod = "initialize")
    // @Lazy
    public EventListenerBean eventListenerBean () {
        return new EventListenerBean();
    }
    public static void main (String... strings) {
        new AnnotationConfigApplicationContext(AppConfig.class);
    }
}

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

EventListenerBean initializing

EventPublisherBean initializing

event received in EventListenerBean : event published from EventPublisherBean

Summarize

If we comment out @ DependsOn ("eventListener"), we may not be sure we get the same result. Try running the main method several times, and occasionally we will see that EventListenerBean does not receive an event. 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 eventListenerBean (). Because EventListenerBean is not loaded at startup, it is loaded when other bean needs it. This time only EventListenerBean is initialized.


EventPublisherBean initializing

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, eventListenerBean is still loaded at startup because @ DependsOn indicates that EventListenerBean is needed.


Related articles: