Note: The Spring source analysis article corresponds to the Spring version 5.1.x

directory

1, an overview of the

2. Custom event-driven programming

2.1 event

2.2 Event listeners

2.3 Event publisher

2.4 Test custom container life cycle events

3, conclusion

1, an overview of the

In order to understand the spring event mechanism, I think it is necessary to first do a simple custom event driven programming demo, because this will help you understand the Spring event mechanism. Of course, this is code that mimics Spring’s event mechanism, but you can forget about spring’s event mechanism and focus on this simple demo.

When looking at custom event-driven programming, it’s important to familiarize yourself with the observer design pattern, because event-driven programming is a concrete implementation of the observer (publish-subscribe) pattern. I recommend another translation of the blog: observer mode – design mode (a), have the need of partners can first learn ha.

The following formal start of the manual code implementation, first put the following code github address:

Github.com/jinyue233/j…

2. Custom event-driven programming

Because this article is a precursor to the Spring event mechanism, you can customize a simple demo that simulates the lifecycle events of a container (think spring container, ServltET container, etc.).

2.1 event

Let’s take a look at the overall architecture diagram of the event, so that we can have an overall understanding of the event, as shown below:

1. Event interface

Interface oriented programming, first define an Event interface, the interface does not have any methods, can be said to be the Event flag interface.

public interface Event extends Serializable {
}
Copy the code

2, AbstractContextEvent

AbstractContextEvent AbstractContextEvent is a basic abstract class for container events. Since events can also carry data, a timestamp property is defined to record when the event occurred.

public class AbstractContextEvent implements Event {
    private static final long serialVersionUID = -6159391039546783871L;

    private final long timestamp = System.currentTimeMillis();

    public final long getTimestamp() {
        return this.timestamp;
    }
}
Copy the code

3, ContextStartEvent

ContextStartEvent Is an AbstractContextEvent implementation class that fires when the container starts. For demo purposes, it doesn’t define any event logic anymore. It’s just a flag event class that represents the start of the container.

public class ContextStartEvent extends AbstractContextEvent {
}
Copy the code

4, ContextRunningEvent

The ContextRunningEvent is AbstractContextEvent implementation class. It is triggered when the container is started. For the sake of simple demo, no event logic is defined here.

public class ContextRunningEvent extends AbstractContextEvent {
}
Copy the code

5, ContextDestroyEvent

ContextDestroyEvent Event is AbstractContextEvent implementation class, which is triggered when the container is destroyed. For the sake of demo simplicity, this event class does not define any event logic anymore.

public class ContextDestroyEvent extends AbstractContextEvent {
}
Copy the code

2.2 Event listeners

Let’s take a look at the overall architecture of the event listener, so that we can have a general understanding of the event listener, as shown below:

EventListener is the base class interface of all event listeners, and is the flag class interface of event listeners, which is implemented by all specific event listeners. ContextListener interface is the container EventListener interface, which inherits EventListener and defines the following EventListener methods:

 void onApplicationEvent(T event);
Copy the code

Then ContextListener interface is three specific vessel life cycle event listener implementation, respectively is ContextStartEventListener (listening container startup ContextStartEvent), ContextRunningEventListener (monitored after container startup runtime ContextRunningEvent) and ContextDestroyEventListener ContextDestroyEvent on container destroyed as ().

Let’s look at the specific code implementation:

public interface EventListener { } public interface ContextListener<T extends AbstractContextEvent> extends EventListener { /** * Handle an application event. * @param event the event to respond to */ void onApplicationEvent(T event); } public class ContextStartEventListener implements ContextListener<AbstractContextEvent> { /** * Handle an application event. * * @param event the event to respond to */ public void onApplicationEvent(AbstractContextEvent event) { if (Event Instanceof ContextStartEvent) {system.out.println (" ContextStartEvent... , start time: "+ event.gettimestamp ()); } } } public class ContextRunningEventListener implements ContextListener<AbstractContextEvent> { /** * Handle an application event. * * @param event the event to respond to */ public void onApplicationEvent(AbstractContextEvent Event) {if (event instanceof ContextRunningEvent) {system.out.println (" The container starts running... ); try { Thread.sleep(3000); System.out.println(" Container finished running... ") ); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class ContextDestroyEventListener implements ContextListener<AbstractContextEvent> { /** * Handle an application event. * * @param event the event to respond to */ public void onApplicationEvent(AbstractContextEvent Event) {if (Event Instanceof ContextDestroyEvent) {system.out.println ("... "+ event.gettimestamp ()); }}}Copy the code

2.3 Event publisher

Take a look at the class diagram:

ApplicationEventMulticaster publish event is the parent of the interface, mainly defines the increase, delete, access operations such as event listener interface, in addition, also defines a publish event method.

SimpleApplicationEventMulticaster ApplicationEventMulticaster event publishing the default implementation of the interface class, mainly to undertake the function of publishing events. ContextListeners are internally maintained, traversed as they are published, and then published to each listener. The Async property is set to determine whether events are broadcast synchronously or asynchronously.

Let’s look at the implementation code:

public interface ApplicationEventMulticaster { void addContextListener(ContextListener<? > listener); void removeContextListener(ContextListener<? > listener); void removeAllListeners(); void multicastEvent(AbstractContextEvent event); } public class SimpleApplicationEventMulticaster implements ApplicationEventMulticaster {/ / whether asynchronous publish event private Boolean async = false; Private Executor taskExecutor = new ThreadPoolExecutor(5, 5, 0, timeUnit.seconds, new LinkedBlockingQueue<Runnable>()); Private List<ContextListener<? >> contextListeners = new ArrayList<ContextListener<? > > (); public void addContextListener(ContextListener<? > listener) { contextListeners.add(listener); } public void removeContextListener(ContextListener<? > listener) { contextListeners.remove(listener); } public void removeAllListeners() { contextListeners.clear(); } public void multicastEvent(AbstractContextEvent event) { doMulticastEvent(contextListeners, event); } private void doMulticastEvent(List<ContextListener<? >> contextListeners, AbstractContextEvent event) { for (ContextListener contextListener : ContextListeners) {async {taskExecutor.execute(() -> invokeListener(contextListener, event)); // new Thread(() -> invokeListener(contextListener, event)).start(); } else {invokeListener(contextListener, event); } } } private void invokeListener(ContextListener contextListener, AbstractContextEvent event) { contextListener.onApplicationEvent(event); } public void setAsync(boolean async) { this.async = async; }}Copy the code

2.4 Test custom container life cycle events

So go ahead and test the code, the following is only to demonstrate the synchronous publishing event function:

public class MockSpringEventTest { @Test public void testContextLifecycleEventInSync() { // The newly built SimpleApplicationEventMulticaster object, And add container lifecycle listener ApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster (); eventMulticaster.addContextListener(new ContextStartEventListener()); eventMulticaster.addContextListener(new ContextRunningEventListener()); eventMulticaster.addContextListener(new ContextDestroyEventListener()); . / / launch container startup event ContextStartEvent eventMulticaster multicastEvent (new ContextStartEvent ()); . / / launch container running event ContextRunningEvent eventMulticaster multicastEvent (new ContextRunningEvent ()); . / / launch container running event ContextDestroyEvent eventMulticaster multicastEvent (new ContextDestroyEvent ()); } @Test public void testContextLifecycleEventInAsync() throws InterruptedException { // The newly built SimpleApplicationEventMulticaster object, And add container lifecycle listener ApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster (); eventMulticaster.addContextListener(new ContextStartEventListener()); eventMulticaster.addContextListener(new ContextRunningEventListener()); eventMulticaster.addContextListener(new ContextDestroyEventListener()); ((SimpleApplicationEventMulticaster) eventMulticaster).setAsync(true); . / / launch container startup event ContextStartEvent eventMulticaster multicastEvent (new ContextStartEvent ()); . / / launch container running event ContextRunningEvent eventMulticaster multicastEvent (new ContextRunningEvent ()); . / / launch container running event ContextDestroyEvent eventMulticaster multicastEvent (new ContextDestroyEvent ()); When CountDownLatch is not used, the main thread exits and the child thread of the non-background thread exits. Junit uses the junit method to latch on to the main thread. When the test thread exits, the non-background thread will exit as well. The main method starts non-background threads that do not // TODO. Is it possible that child B started by child A (not main thread) will exit as child A exits? CountDownLatch = new CountDownLatch(1); countDownLatch.await(); } public static void main (String [] args) throws InterruptedException {/ / new SimpleApplicationEventMulticaster object, And add container lifecycle listener ApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster (); eventMulticaster.addContextListener(new ContextStartEventListener()); eventMulticaster.addContextListener(new ContextRunningEventListener()); eventMulticaster.addContextListener(new ContextDestroyEventListener()); ((SimpleApplicationEventMulticaster) eventMulticaster).setAsync(true); . / / launch container startup event ContextStartEvent eventMulticaster multicastEvent (new ContextStartEvent ()); . / / launch container running event ContextRunningEvent eventMulticaster multicastEvent (new ContextRunningEvent ()); . / / launch container running event ContextDestroyEvent eventMulticaster multicastEvent (new ContextDestroyEvent ()); }}Copy the code

By running the test method testContextLifecycleEventInSync (), run results screenshot as follows:

3, conclusion

There you have it: a simple demo of custom event-driven programming.

This is just a lead-up to the source code analysis of Spring’s event mechanism. See the next post for the real source code analysis:

Spring event Mechanism source code Analysis (2)

Original is not easy, please give me a thumbs up.