Take off the coat of the Spring event monitoring mechanism, it turns out to be the observer mode

foreword

Spring provides a set of default event monitoring mechanisms, which are used when the container is initialized. At the same time, Spring also provides the interface extension capability of the event monitoring mechanism, based on which developers can quickly implement custom event monitoring functions.

Spring's event monitoring mechanism is an extension based on JDK event monitoring, and it is also a further abstraction and improvement on the typical observer model. Therefore, combining Spring's event monitoring mechanism and observer mode to learn can achieve the perfect integration of theory and practice.

This article takes the observer mode and the Spring event monitoring mechanism as the starting point, and combines specific examples to systematically learn and practice the two.

Observer pattern

Observer Pattern (Observer Pattern), also known as publish-subscribe pattern (Publish/Subscribe).

Whether it is the observer mode or Spring's event listening mechanism, it is essentially defining a one-to-many dependency relationship between objects, so that whenever an object (observed/event) changes state, all objects that depend on it (observers/event listeners) are notified and are automatically updated.

The advantage of the observer mode is that the observer and the observed are abstractly coupled, and it is very easy to expand whether it is a new observer or an observed. This is also in line with the " opening and closing principle " advocated by object-oriented : open for extension and closed for modification .

Observer mode is suitable for the following three types of scenarios:

  • Behavior scenarios are associated, and the association is detachable.
  • Event multi-level trigger scenarios.
  • Cross-system message exchange scenarios, such as the processing mechanism of message queues.

In the process of use, the issues of development efficiency and operating efficiency should also be considered comprehensively. Usually, one observed object corresponds to multiple observers, so there will be a certain degree of complexity in the process of development and debugging.

At the same time, because the observed objects are associated and multi-level split, that is, there will be multiple observers, and the notification of Java messages (and Spring's event monitoring mechanism) is executed sequentially by default. If one of the observers executes for too long Long or stuck, will inevitably affect the overall efficiency. At this point, asynchronous processing needs to be considered.

Observer role definition

Observer mode is a typical publish-subscribe model, which mainly involves four roles:

  • Abstract observer role: internally holds references to all observer roles, and provides external functions for adding, removing observer roles, and notifying all observers;
  • Specific observer roles: when the status changes, all observer roles will be notified;
  • Abstract observer role: abstract some common methods of specific observer roles, such as state change methods;
  • Concrete observer roles: methods for implementing abstract observer roles;

The UML class diagram shows the class observer pattern roughly as follows:

Observer pattern class diagram

Use specific code to show the implementation of the observer pattern.

First, define an abstract observer.

/**
 * 抽象观察者角色
 * @author sec
 **/
public abstract class AbstractObserver {

	/**
	 * 接收消息
	 * @param context 消息内容
	 */
	public abstract void receiveMsg(String context);

}

Second, define the abstract observer.

/**
 * 抽象主题(抽象被观察者角色)
 * @author sec
 **/
public abstract class AbstractSubject {

	/**
	 * 持有所有抽象观察者角色的集合引用
	 */
	private final List<AbstractObserver> observers = new ArrayList<>();

	/**
	 * 添加一个观察者
	 * @param observer 观察者
	 */
	public void addObserver(AbstractObserver observer){
		observers.add(observer);
	}

	/**
	 * 移除一个观察者
	 * @param observer 观察者
	 */
	public void removeObserver(AbstractObserver observer){
		observers.remove(observer);
	}

	/**
	 * 通知所有的观察者,执行观察者更新方法
	 * @param context 通知内容
	 */
	public void notifyObserver(String context){
		observers.forEach(observer -> observer.receiveMsg(context));
	}
}

Third, define the specific observer and realize the abstract observer.

/**
 * 具体被观察者
 * @author sec
 **/
public class ConcreteSubject extends AbstractSubject{
	
	/**
	 * 被观察者发送消息
	 * @param context 消息内容
	 */
	public void sendMsg(String context){
		System.out.println("具体被观察者角色发送消息: " + context);
		super.notifyObserver(context);
	}
}

Fourth, define specific observers and realize abstract observers.

/**
 * 具体观察者角色实现类
 * @author sec
 **/
public class ConcreteObserver extends AbstractObserver{

	@Override
	public void receiveMsg(String context) {
		System.out.println("具体观察者角色接收消息: " + context);
	}
}

Fifth, use the demo class.

public class ObserverPatternTest {

	public static void main(String[] args) {
		ConcreteSubject subject = new ConcreteSubject();
		subject.addObserver(new ConcreteObserver());
		subject.sendMsg("Hello World!");
	}
}

Execute the above method, the console print log is:

具体被观察者角色发送消息: Hello World!
具体观察者角色接收消息: Hello World!

In the above code implementation, after the observer sends a message, the observer receives the specific message. If multiple observers are added, they will all receive the message. That is to say, whenever an object (observed/event) changes state, all objects (observers/event listeners) that depend on it will be notified and updated automatically.

Event Mechanism in Java

I talked about the observer mode earlier, and here is a look at the event mechanism in Java.

In JDK 1.1 and later versions, the event processing model adopts the delegated event model (DelegationEvent Model, DEM) based on the observer mode, that is, the event triggered by a Java component is not handled by the object that triggered the event itself, but delegated to Separate event handling objects are responsible.

This is not to say that the event model is based on Observer and Observable. The event model has nothing to do with Observer and Observable. Observer and Observable are just an implementation of the observer pattern.

The event mechanism in Java has three roles involved:

  • Event Source: Event source, the subject that initiates the event.

  • Event Object: The event status object, the information carrier to be passed, can be the event source itself, and generally exists as a parameter in the method of the listerner. All event state objects will be derived from EventObject in Java;

  • Event Listener: Event listener, when the EventObject is detected, it will call the corresponding method for processing. All event listener interfaces must extend the EventListener interface;

The UML class diagram shows the class event pattern roughly as follows:

event listener

In the above UML diagram, EventObject is generally passed in as a parameter of the Listener processing method, and EventSource is the trigger of the event. The relevant Listener is registered through this object, and then the event is triggered to the Listener.

Through the comparison of UML diagrams, it can be seen that the event monitoring mode and the observer mode are similar, they belong to the same type of mode, both belong to the callback mechanism, and actively push messages, but there are differences in usage scenarios.

The observer (Observer) is equivalent to the event listener (listener), and the observed (Observable) is equivalent to the event source and event. The event monitoring is more complicated than the observer mode, and the existence of the EventSource role is added.

Use specific code to show the implementation of the event mechanism in Java.

First, define the event object.

/**
 * 事件对象
 *
 * @author sec
 **/
public class DoorEvent extends EventObject {

	private int state;

	/**
	 * Constructs a prototypical Event.
	 *
	 * @param source The object on which the Event initially occurred.
	 * @throws IllegalArgumentException if source is null.
	 */
	public DoorEvent(Object source) {
		super(source);
	}

	public DoorEvent(Object source, int state) {
		super(source);
		this.state = state;
	}

	// 省略getter/setter方法 
}

Second, define the event listener interface.

/**
 * 事件监听器接口
 *
 * @author sec
 **/
public interface DoorListener extends EventListener {

	/**
	 * 门处理事件
	 * @param doorEvent 事件
	 */
	void doorEvent(DoorEvent doorEvent);
}

Third, define the implementation class of the event listener.

public class CloseDoorListener implements DoorListener{
	@Override
	public void doorEvent(DoorEvent doorEvent) {
		if(doorEvent.getState() == -1){
			System.out.println("门关上了");
		}
	}
}

public class OpenDoorListener implements DoorListener{
	@Override
	public void doorEvent(DoorEvent doorEvent) {
		if(doorEvent.getState() == 1){
			System.out.println("门打开了");
		}
	}
}

Two event listener classes for opening and closing the door are implemented here.

Fourth, define the event source EventSource.

public class EventSource {

	//监听器列表,监听器的注册则加入此列表
	private Vector<DoorListener> listenerList = new Vector<>();

	//注册监听器
	public void addListener(DoorListener eventListener) {
		listenerList.add(eventListener);
	}

	//撤销注册
	public void removeListener(DoorListener eventListener) {
		listenerList.remove(eventListener);
	}

	//接受外部事件
	public void notifyListenerEvents(DoorEvent event) {
		for (DoorListener eventListener : listenerList) {
			eventListener.doorEvent(event);
		}
	}
}

Fifth, the test class.

public class EventTest {

	public static void main(String[] args) {
		EventSource eventSource = new EventSource();
		eventSource.addListener(new CloseDoorListener());
		eventSource.addListener(new OpenDoorListener());

		eventSource.notifyListenerEvents(new DoorEvent("关门事件", -1));
		eventSource.notifyListenerEvents(new DoorEvent("开门时间", 1));
	}
}

Execute the test class, and the console prints:

门关上了
门打开了

Event fired successfully.

Event mechanism in Spring

After understanding the observer mode and Java's event mechanism, let's take a look at the event mechanism in Spring. In the Spring container, the event monitoring mechanism is implemented through ApplicationEventand ApplicationListenerinterfaces. Every time an Event event is published to the Spring container, the corresponding Listener will be notified. By default, Spring's event listening mechanism is synchronous.

Spring's event listening consists of three parts:

  • Event (ApplicationEvent): This class inherits from EventObject in JDK and is responsible for corresponding listeners. The occurrence of an event at the event source is the reason why a specific event listener is triggered;
  • Listener (ApplicationListener): This class inherits from EventListener in JDK and corresponds to the observer in the observer mode. The listener listens to specific events and internally defines the response logic after the event occurs;
  • Event Publisher (ApplicationEventPublisher): Corresponds to the observer/subject in the observer mode, responsible for notifying the observer, providing an interface for publishing events and adding and deleting event listeners, maintaining the mapping relationship between events and event listeners, and Responsible for notifying relevant listeners when an event occurs.

From the above analysis, it can be seen that Spring's event mechanism is not only an implementation of the observer mode, but also implements the event interface provided by JDK. At the same time, in addition to the publisher and the listener, there is also a role of EventMulticaster, which is responsible for forwarding the event to the listener.

The workflow of the Spring event mechanism is as follows:

Spring event mechanism

In the above process, the publisher calls applicationEventPublisher.publishEvent(msg) to send the event to EventMultiCaster. EventMultiCaster registers all Listeners, and it will forward to that Listener according to the event type.

Some standard events are provided in Spring, such as: ContextRefreshEvent, ContextStartedEvent, ContextStoppedEvent, ContextClosedEvent, RequestHandledEvent, etc.

Regarding the specific implementation of the Spring event mechanism and the functions of these standard events, you can learn by reading the source code, which will not be detailed here.

Let's take a look at the source code of several roles involved in the Spring event mechanism and the subsequent practice based on them.

First, the event (ApplicationEvent).

public abstract class ApplicationEvent extends EventObject {

	/** use serialVersionUID from Spring 1.2 for interoperability. */
	private static final long serialVersionUID = 7099057708183571937L;

	/** System time when the event happened. */
	private final long timestamp;

	/**
	 * Create a new {@code ApplicationEvent}.
	 * @param source the object on which the event initially occurred or with
	 * which the event is associated (never {@code null})
	 */
	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}

	/**
	 * Return the system time in milliseconds when the event occurred.
	 */
	public final long getTimestamp() {
		return this.timestamp;
	}

}

The event can be compared to the role of the observer implementation class in the observer, which inherits from JDK's EventObject. The above standard events in Spring are directly or indirectly inherited from this class.

Second, the event publisher (ApplicationEventPublisher).

@FunctionalInterface
public interface ApplicationEventPublisher {

	default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}

	void publishEvent(Object event);
}

By implementing the ApplicationEventPublisher interface and rewriting the publishEvent() method, you can customize the logic of event publishing. ApplicationContext inherits the ApplicationEventPublisher interface. Therefore, we can implement the ApplicationContextAware interface, inject the ApplicationContext, and then implement the event publishing function through the publishEvent() method of the ApplicationContext.

The ApplicationContext container itself only provides the interface publishEvent() for event publishing, and the real work is entrusted to the ApplicationEventMulticaster object inside the specific container. The ApplicationEventMulticaster object can be compared to the abstract observer role in the observer pattern, and is responsible for holding references to all observer collections, dynamically adding, and removing observer roles.

Third, the event listener (ApplicationListener).

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

	/**
	 * Handle an application event.
	 * @param event the event to respond to
	 */
	void onApplicationEvent(E event);

}

The event listener (ApplicationListener) corresponds to the specific observer role in the observer pattern. When the event is published, the logic of the event listener will be executed. By implementing the ApplicationListener interface and rewriting the onApplicationEvent() method, you can listen to the events published by the event publisher.

Spring event listening case

The following is a specific case code to illustrate how to customize the implementation of Spring event monitoring.

First, define custom event objects, integrated from ApplicationEvent.

public class MyEvent extends ApplicationEvent {
	/**
	 * Create a new {@code ApplicationEvent}.
	 *
	 * @param source the object on which the event initially occurred or with
	 *               which the event is associated (never {@code null})
	 */
	public MyEvent(Object source) {
		super(source);
	}

	private String context;

	public MyEvent(Object source, String context){
		super(source);
		this.context = context;
	}

	public String getContext() {
		return context;
	}

	public void setContext(String context) {
		this.context = context;
	}
}

Second, customize the ApplicationListener event listener.

@Component
public class MyApplicationListener implements ApplicationListener<MyEvent> {
	@Override
	public void onApplicationEvent(MyEvent event) {
		// 监听到具体事件,处理对应具体逻辑
		System.out.println("event.getContext() = " + event.getContext());
	}

}

In addition to the above method based on implementing the ApplicationListener interface, it can also be implemented using the **@EventListener** annotation. The implementation example is as follows:

@Component
public class MyApplicationListener{

    // 通过注解实现监听器
    @EventListener
    public void handleMyEvent(MyEvent event){
        // 监听到具体事件,处理对应具体逻辑
				System.out.println("event.getContext() = " + event.getContext());
    }
}

Third, use and unit testing.

@Slf4j
@SpringBootTest
public class SpringEventTest {

	@Autowired
	private ApplicationEventPublisher eventPublisher;

	@Test
	void testEvent() {
		eventPublisher.publishEvent(new MyEvent("自定义事件", "Hello World!"));
	}
}

Execute the unit test, and you can see the corresponding event information printed on the console.

Through the above method, we have successfully implemented the Spring-based event monitoring mechanism, but there is still a problem: synchronous processing. By default, the above events are based on synchronous processing, if one of the listeners is blocked, then the entire thread will be in a waiting state.

So, how to handle listening events asynchronously? It only takes two steps.

@AsyncThe first step is to add annotations to the listener class or method , for example:

@Component
@Async
public class MyApplicationListener implements ApplicationListener<MyEvent> {
	@Override
	public void onApplicationEvent(MyEvent event) {
		// 监听到具体事件,处理对应具体逻辑
		System.out.println("event.getContext() = " + event.getContext());
	}

}

@EnableAsyncThe second step is to add annotations to the SpringBoot startup class (here the SpringBoot project is taken as an example) , for example:

@SpringBootApplication
@EnableAsync
public class SpringBootMainApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringBootMainApplication.class, args);
	}
}

At this point, the asynchronous monitoring function can be realized. Of course, @Asyncannotations can also specify the thread pool we have configured to handle asynchronous requests, and the initialization of the number of threads will not be demonstrated here.

summary

This article takes you from the observer mode, Java event mechanism to Spring's event monitoring mechanism, and combines the three together to explain. Through this case, we can actually experience some empirical knowledge. For example, the seemingly complicated implementation of the Spring event monitoring mechanism is just an implementation of the observer mode, which integrates the Java event mechanism. This is what is called integration.

If we simply learn a certain design pattern, we may only use and recognize its simple implementation. In practice, we often make variants of the design pattern, and even integrate the advantages of multiple design patterns. This is learning and using. I hope that through this article, you can have a deeper understanding of the above three.

Guess you like

Origin blog.csdn.net/wo541075754/article/details/127859238