7
头图

Author: Xiao Fu Ge
Blog: https://bugstack.cn

Precipitate, share, and grow, so that you and others can gain something! 😄

I. Introduction

can decoupling, what an important thing!

Wrestling the cup as a sign, watching me wink, and seeing the fire on the south side, is this gaha? This is actually a decoupling of leads and bombs through the spread of things. It is just such a decoupling. How many reckless villagers have been placed on it, it has robbed the court and usurped the power of the army!

Such decoupling scenarios are also used very frequently in the design of Internet development, such as: here needs a registration completion event push message, users place an order and I will send an MQ, can ship after receiving my payment message And so on, all rely on components such as event subscription and publishing and MQ messages to handle the decoupling of calls between systems, and ultimately improve the load capacity of the overall system architecture through decoupling.

In fact, the idea of decoupling can be understood as the specific use effect of the observer pattern in the design pattern. In the observer pattern, when there is a one-to-many relationship between objects, the observer pattern is used, which is a way of defining objects. For many dependencies, when the state of an object changes, all objects that depend on it are notified and automatically updated. 160eba22f8e76a This reminds me of my monthly license plate lottery, and will send me a message that I did not ! !

2. Goal

There is an Event function in Spring, which can provide event definition, release, and monitor events to complete some custom actions. For example, you can define a new user registration event. When a user completes the registration, some coupons and SMS reminders will be sent to the user in the event monitoring. This operation can separate the registration belonging to the basic function from the corresponding policy service. , Reduce the coupling of the system. In the future, the expansion of registration services, such as the need to add risk control strategies, add real-name authentication, and determine user attributes will not affect the actions performed after successful registration.

So in this chapter, we need to design and implement Spring Event's container events and event listener functions in the form of observer mode. Finally, we can define, monitor, and publish our own event information in the now implemented Spring framework.

Three, the plan

In fact, the design of the event itself is the realization of an observer mode. What it wants to solve is the problem of notifying other objects of an object's state change, and it must take into account the ease of use and low coupling to ensure a high degree of collaboration.

In terms of functional implementation, we need to define event classes, event listeners, and event releases. The functions of these classes need to be combined with Spring's AbstractApplicationContext#refresh() to facilitate the processing of event initialization and registration of event listeners. The overall design structure is as follows:

  • In the entire function implementation process, it is still necessary AbstractApplicationContext , including: initializing the event publisher, registering the event listener, and publishing the container refresh completion event.
  • Use the observer pattern to define event classes, listener classes, and publish classes. At the same time, it is necessary to complete the function of a broadcaster. When an event push is received, the analysis and processing are in line with the events that are of interest to the listener event receiver, which is to use isAssignableFrom to make judgments.
  • isAssignableFrom is similar to instanceof, but isAssignableFrom is used to determine the relationship between the subclass and the parent class, or the relationship between the implementation class of the interface and the interface. By default, the ultimate parent class of all classes is Object. If the result of A.isAssignableFrom(B) is true, it proves that B can be converted into A, that is, A can be converted from B.

Fourth, realize

1. Engineering structure

small-spring-step-10
└── src
    ├── main
    │   └── java
    │       └── cn.bugstack.springframework
    │           ├── beans
    │           │   ├── factory
    │           │   │   ├── config
    │           │   │   │   ├── AutowireCapableBeanFactory.java
    │           │   │   │   ├── BeanDefinition.java
    │           │   │   │   ├── BeanFactoryPostProcessor.java
    │           │   │   │   ├── BeanPostProcessor.java
    │           │   │   │   ├── BeanReference.java
    │           │   │   │   ├── ConfigurableBeanFactory.java
    │           │   │   │   └── SingletonBeanRegistry.java
    │           │   │   ├── support
    │           │   │   │   ├── AbstractAutowireCapableBeanFactory.java
    │           │   │   │   ├── AbstractBeanDefinitionReader.java
    │           │   │   │   ├── AbstractBeanFactory.java
    │           │   │   │   ├── BeanDefinitionReader.java
    │           │   │   │   ├── BeanDefinitionRegistry.java
    │           │   │   │   ├── CglibSubclassingInstantiationStrategy.java
    │           │   │   │   ├── DefaultListableBeanFactory.java
    │           │   │   │   ├── DefaultSingletonBeanRegistry.java
    │           │   │   │   ├── DisposableBeanAdapter.java
    │           │   │   │   ├── FactoryBeanRegistrySupport.java
    │           │   │   │   ├── InstantiationStrategy.java
    │           │   │   │   └── SimpleInstantiationStrategy.java  
    │           │   │   ├── support
    │           │   │   │   └── XmlBeanDefinitionReader.java
    │           │   │   ├── Aware.java
    │           │   │   ├── BeanClassLoaderAware.java
    │           │   │   ├── BeanFactory.java
    │           │   │   ├── BeanFactoryAware.java
    │           │   │   ├── BeanNameAware.java
    │           │   │   ├── ConfigurableListableBeanFactory.java
    │           │   │   ├── DisposableBean.java
    │           │   │   ├── FactoryBean.java
    │           │   │   ├── HierarchicalBeanFactory.java
    │           │   │   ├── InitializingBean.java
    │           │   │   └── ListableBeanFactory.java
    │           │   ├── BeansException.java
    │           │   ├── PropertyValue.java
    │           │   └── PropertyValues.java 
    │           ├── context  
    │           │   ├── event
    │           │   │   ├── AbstractApplicationEventMulticaster.java 
    │           │   │   ├── ApplicationContextEvent.java 
    │           │   │   ├── ApplicationEventMulticaster.java 
    │           │   │   ├── ContextClosedEvent.java 
    │           │   │   ├── ContextRefreshedEvent.java 
    │           │   │   └── SimpleApplicationEventMulticaster.java 
    │           │   ├── support
    │           │   │   ├── AbstractApplicationContext.java 
    │           │   │   ├── AbstractRefreshableApplicationContext.java 
    │           │   │   ├── AbstractXmlApplicationContext.java 
    │           │   │   ├── ApplicationContextAwareProcessor.java 
    │           │   │   └── ClassPathXmlApplicationContext.java 
    │           │   ├── ApplicationContext.java 
    │           │   ├── ApplicationContextAware.java 
    │           │   ├── ApplicationEvent.java 
    │           │   ├── ApplicationEventPublisher.java 
    │           │   ├── ApplicationListener.java 
    │           │   └── ConfigurableApplicationContext.java
    │           ├── core.io
    │           │   ├── ClassPathResource.java 
    │           │   ├── DefaultResourceLoader.java 
    │           │   ├── FileSystemResource.java 
    │           │   ├── Resource.java 
    │           │   ├── ResourceLoader.java 
    │           │   └── UrlResource.java
    │           └── utils
    │               └── ClassUtils.java
    └── test
        └── java
            └── cn.bugstack.springframework.test
                ├── event
                │   ├── ContextClosedEventListener.java
                │   ├── ContextRefreshedEventListener.java
                │   ├── CustomEvent.java
                │   └── CustomEventListener.java
                └── ApiTest.java

project source code : public account "bugstack wormhole stack", reply: Spring column, get the complete source code

The container event and event listener realize the class relationship, as shown in Figure 11-2

图 10-2

  • The above entire class relationship diagram is based on the realization of event definition, release, and monitoring functions, and the use of AbstractApplicationContext#refresh to register and process the related content of the event.
  • In the process of implementation, the main focus is to extend the spring context package, and the implementation of events is also extended under this package. Of course, it can be seen that all the current implementation content is still based on IOC.
  • The ApplicationContext container inherits the event publishing function interface ApplicationEventPublisher, and provides the event monitoring function in the implementation class.
  • The ApplicationEventMulticaster interface is a broadcaster for registering listeners and publishing events, and provides methods for adding, removing, and publishing events.
  • Finally, the container close event is released. This still needs to be extended to the AbstractApplicationContext#close method, which is implemented by the hook registered to the virtual machine.

2. Define and implement events

cn.bugstack.springframework.context.ApplicationEvent

public abstract class ApplicationEvent extends EventObject {

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

}
  • The abstract class ApplicationEvent with event functions is defined by inheriting java.util.EventObject, and all subsequent event classes need to inherit this class.

cn.bugstack.springframework.context.event.ApplicationContextEvent

public class ApplicationContextEvent extends ApplicationEvent {

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

    /**
     * Get the <code>ApplicationContext</code> that the event was raised for.
     */
    public final ApplicationContext getApplicationContext() {
        return (ApplicationContext) getSource();
    }

}

cn.bugstack.springframework.context.event.ContextClosedEvent

public class ContextClosedEvent extends ApplicationContextEvent{

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

}

cn.bugstack.springframework.context.event.ContextRefreshedEvent

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

}
  • ApplicationContextEvent is an abstract class that defines events. All events including closing, refreshing, and events implemented by the user need to inherit this class.
  • ContextClosedEvent and ContextRefreshedEvent are two event classes implemented by the Spring framework, which can be used to monitor refresh and close actions.

3. Event Broadcaster

cn.bugstack.springframework.context.event.ApplicationEventMulticaster

public interface ApplicationEventMulticaster {

    /**
     * Add a listener to be notified of all events.
     * @param listener the listener to add
     */
    void addApplicationListener(ApplicationListener<?> listener);

    /**
     * Remove a listener from the notification list.
     * @param listener the listener to remove
     */
    void removeApplicationListener(ApplicationListener<?> listener);

    /**
     * Multicast the given application event to appropriate listeners.
     * @param event the event to multicast
     */
    void multicastEvent(ApplicationEvent event);

}
  • In the event broadcaster, methods for adding and deleting monitors and a method for broadcasting events multicastEvent are defined in the event broadcaster. The final push of the time message will also go through this interface method to process who should receive the event.

cn.bugstack.springframework.context.event.AbstractApplicationEventMulticaster

public abstract class AbstractApplicationEventMulticaster implements ApplicationEventMulticaster, BeanFactoryAware {

    public final Set<ApplicationListener<ApplicationEvent>> applicationListeners = new LinkedHashSet<>();

    private BeanFactory beanFactory;

    @Override
    public void addApplicationListener(ApplicationListener<?> listener) {
        applicationListeners.add((ApplicationListener<ApplicationEvent>) listener);
    }

    @Override
    public void removeApplicationListener(ApplicationListener<?> listener) {
        applicationListeners.remove(listener);
    }

    @Override
    public final void setBeanFactory(BeanFactory beanFactory) {
        this.beanFactory = beanFactory;
    }

    protected Collection<ApplicationListener> getApplicationListeners(ApplicationEvent event) {
        LinkedList<ApplicationListener> allListeners = new LinkedList<ApplicationListener>();
        for (ApplicationListener<ApplicationEvent> listener : applicationListeners) {
            if (supportsEvent(listener, event)) allListeners.add(listener);
        }
        return allListeners;
    }

    /**
     * 监听器是否对该事件感兴趣
     */
    protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {
        Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();

        // 按照 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy 不同的实例化类型,需要判断后获取目标 class
        Class<?> targetClass = ClassUtils.isCglibProxyClass(listenerClass) ? listenerClass.getSuperclass() : listenerClass;
        Type genericInterface = targetClass.getGenericInterfaces()[0];

        Type actualTypeArgument = ((ParameterizedType) genericInterface).getActualTypeArguments()[0];
        String className = actualTypeArgument.getTypeName();
        Class<?> eventClassName;
        try {
            eventClassName = Class.forName(className);
        } catch (ClassNotFoundException e) {
            throw new BeansException("wrong event class name: " + className);
        }
        // 判定此 eventClassName 对象所表示的类或接口与指定的 event.getClass() 参数所表示的类或接口是否相同,或是否是其超类或超接口。
        // isAssignableFrom是用来判断子类和父类的关系的,或者接口的实现类和接口的关系的,默认所有的类的终极父类都是Object。如果A.isAssignableFrom(B)结果是true,证明B可以转换成为A,也就是A可以由B转换而来。
        return eventClassName.isAssignableFrom(event.getClass());
    }

}
  • AbstractApplicationEventMulticaster is an abstraction of the public methods of event broadcasters. Some basic functions can be implemented in this class to avoid all the details that need to be dealt with directly to implement the interface.
  • Except for the general methods like addApplicationListener and removeApplicationListener, this class mainly deals with getApplicationListeners and supportsEvent.
  • The getApplicationListeners method is mainly to extract the listener handlers that conform to the broadcast event, and the specific filtering action is in the supportsEvent method.
  • In the supportsEvent method, it mainly includes the need to obtain the target Class for different instantiations of Cglib and Simple, the Cglib proxy class needs to obtain the Class of the parent class, and it is not needed for ordinary instantiation. The next step is to extract the interface and the corresponding ParameterizedType and eventClassName to facilitate the final confirmation of the relationship between the subclass and the parent class, so as to prove that the event belongs to this conforming class. can refer to the comments in the code

supportsEvent method running screenshot

  • As you can see in the code debugging, eventClassName and event.getClass() are finally true under the judgment of isAssignableFrom
  • About CglibSubclassingInstantiationStrategy, SimpleInstantiationStrategy, you can try to replace the verification in the AbstractApplicationContext class.

4. Definition and implementation of event publisher

cn.bugstack.springframework.context.ApplicationEventPublisher

public interface ApplicationEventPublisher {

    /**
     * Notify all listeners registered with this application of an application
     * event. Events may be framework events (such as RequestHandledEvent)
     * or application-specific events.
     * @param event the event to publish
     */
    void publishEvent(ApplicationEvent event);

}
  • ApplicationEventPublisher is an event publishing interface, all events need to be published from this interface.

cn.bugstack.springframework.context.support.AbstractApplicationContext

public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {

    public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";

    private ApplicationEventMulticaster applicationEventMulticaster;

    @Override
    public void refresh() throws BeansException {

        // 6. 初始化事件发布者
        initApplicationEventMulticaster();

        // 7. 注册事件监听器
        registerListeners();

        // 9. 发布容器刷新完成事件
        finishRefresh();
    }

    private void initApplicationEventMulticaster() {
        ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, applicationEventMulticaster);
    }

    private void registerListeners() {
        Collection<ApplicationListener> applicationListeners = getBeansOfType(ApplicationListener.class).values();
        for (ApplicationListener listener : applicationListeners) {
            applicationEventMulticaster.addApplicationListener(listener);
        }
    }

    private void finishRefresh() {
        publishEvent(new ContextRefreshedEvent(this));
    }

    @Override
    public void publishEvent(ApplicationEvent event) {
        applicationEventMulticaster.multicastEvent(event);
    }

    @Override
    public void close() {
        // 发布容器关闭事件
        publishEvent(new ContextClosedEvent(this));

        // 执行销毁单例bean的销毁方法
        getBeanFactory().destroySingletons();
    }

}
  • In the abstract application context AbstractApplicationContext#refresh, the initialization event publisher, the registration event listener, and the publishing container refresh completion event are mainly added. Three methods are used to handle event operations.
  • Initialization event publisher (initApplicationEventMulticaster) is mainly used to instantiate a SimpleApplicationEventMulticaster, which is an event broadcaster.
  • Register event listeners (registerListeners), get all the event configuration Bean objects loaded from spring.xml through the getBeansOfType method.
  • Publish the container refresh completion event (finishRefresh), which publishes the event after the first server is started. This event is published through publishEvent, in fact, it calls the applicationEventMulticaster.multicastEvent(event); method.
  • Finally, in a close method, a new container close event is released. publishEvent(new ContextClosedEvent(this));

Five, test

1. Create an event and listener

cn.bugstack.springframework.test.event.CustomEvent

public class CustomEvent extends ApplicationContextEvent {

    private Long id;
    private String message;

    /**
     * Constructs a prototypical Event.
     *
     * @param source The object on which the Event initially occurred.
     * @throws IllegalArgumentException if source is null.
     */
    public CustomEvent(Object source, Long id, String message) {
        super(source);
        this.id = id;
        this.message = message;
    }

    // ...get/set
}
  • Create a custom event, you can add your own desired input parameter information in the constructor of the event class. This event class will eventually be completed and brought to the listener, so all the properties you add will be obtained.

cn.bugstack.springframework.test.event.CustomEventListener

public class CustomEventListener implements ApplicationListener<CustomEvent> {

    @Override
    public void onApplicationEvent(CustomEvent event) {
        System.out.println("收到:" + event.getSource() + "消息;时间:" + new Date());
        System.out.println("消息:" + event.getId() + ":" + event.getMessage());
    }

}
  • This is a listener used to monitor CustomEvent events. Here you can handle the operations you want, such as sending coupons and SMS notifications after some users register.
  • In addition, it is about ContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent> and ContextClosedEventListener implements ApplicationListener<ContextClosedEvent> listeners, which will not be demonstrated here. You can refer to the source code.

2. Configuration file

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <bean class="cn.bugstack.springframework.test.event.ContextRefreshedEventListener"/>

    <bean class="cn.bugstack.springframework.test.event.CustomEventListener"/>

    <bean class="cn.bugstack.springframework.test.event.ContextClosedEventListener"/>

</beans>
  • Three event listeners are configured in spring.xml to monitor refresh, monitor custom events, and monitor close events.

3. Unit Testing

public class ApiTest {

    @Test
    public void test_event() {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
        applicationContext.publishEvent(new CustomEvent(applicationContext, 1019129009086763L, "成功了!"));

        applicationContext.registerShutdownHook();
    }

}
  • By using the newly added release event interface method of applicationContext, a custom event CustomEvent is released, and the corresponding parameter information is transparently transmitted.

test result

刷新事件:cn.bugstack.springframework.test.event.ContextRefreshedEventListener$$EnhancerByCGLIB$$440a36f5
收到:cn.bugstack.springframework.context.support.ClassPathXmlApplicationContext@71c7db30消息;时间:22:32:50
消息:1019129009086763:成功了!
关闭事件:cn.bugstack.springframework.test.event.ContextClosedEventListener$$EnhancerByCGLIB$$f4d4b18d

Process finished with exit code 0
  • From the test results, we can see that the events and monitors defined by ourselves, as well as the event information of the monitoring system, can all be output in the console. You can also try to add some other event behaviors, and debug the code to learn the observer mode.

Six, summary

  • Throughout the learning process of handwriting the Spring framework, you can gradually see the use of many design patterns, such as: simple factory BeanFactory, factory method FactoryBean, strategy mode to access resources, and now there is a specific use of observer mode. So in the process of learning Spring, you should pay more attention to the application of design patterns. This is the core and the focus of learning to understand the code.
  • Then the implementation process of the observer pattern in this chapter mainly includes event definition, event monitoring and event publishing. After publishing, the listener will receive its own event content and take corresponding processing actions according to the matching strategy. In fact, we often use this observer pattern everyday, but after combining with Spring, in addition to learning design patterns, we can also learn how to combine the implementation of the corresponding observer with the application context.
  • All the technologies, designs, and ideas learned in Spring can be combined with actual business development, and these seemingly more code modules are actually expanded little by little according to their respective responsibilities. In your own learning process, you can first try to complete these framework functions, and compare and reference Spring source code through debugging a little bit, and finally you will slowly master these design and coding capabilities.

Seven, series recommendation


小傅哥
4.7k 声望28.4k 粉丝

CodeGuide | 程序员编码指南 - 原创文章、案例源码、资料书籍、简历模版等下载。