Blog: bugstack.cn

Precipitation, share, grow, let yourself and others can gain something! 😄

One, foreword

How important it is to be able to decouple!

Throw the cup for the horn, look at my eyes to act, see the fire on the south, this is in gaha? This is actually decoupling the fuse and the bomb through the propagation of things, just such a decoupling, it put in many village people, robbed the legal field, usurp the military power!

Such decoupling scenarios are also frequently used in Internet development designs, such as: Here need a registration complete event push message, user order I will send a MQ, pay after receiving my message can delivery, etc., are relying on the event subscription and release and MQ message such components, to deal with the system called decoupling between the final by decoupling the way to improve load capacity of the overall system architecture.

Actually can understand the thoughts for decoupling design mode to use the observer pattern effect, in the observer pattern when there is a one-to-many relationship between objects, using the observer pattern, it is a kind of definition a one-to-many dependency between objects, when an object’s state is changed, all of the object depends on its notified and is automatically updated. This reminds me of my monthly license plate lottery, which will push me a message that I did not win this month!!

Second, the target

There is an Event function in Spring that provides the ability to define, publish, and listen for events to perform custom actions. For example, you can define a new user registration event, and send some coupons and SMS reminders to the user in the event monitor after the user completes the registration. Such operation can separate the registration of basic functions from the corresponding policy service, and reduce the coupling of the system. In the future, the extension of the registration service, such as adding risk control policies, adding real-name authentication, and judging user attributes, will not affect the actions to be performed after the dependent registration is successful.

In this chapter, we need to design and implement the container Event and Event listener functions of Spring Event in the way of observer mode. Finally, we can define, monitor and publish our own Event information in the Spring framework now implemented.

Three,

In fact, the design of the event itself is an implementation of the observer mode. What it needs to solve is the problem that the state change of an object notifies other objects, and it should take into account the ease of use and low coupling to ensure a high degree of collaboration.

On the functions we need to define events, event listeners, release, and the function of these classes requires a combination of the Spring AbstractApplicationContext# refresh (), in order to handle events initialization, and the operation of the registered event listeners. The overall design structure is as follows:

  • In the whole process of functional implementation, the user – oriented application context is still neededAbstractApplicationContextTo add related event content, including: initialize event publisher, register event listener, publish container refresh completion event.
  • The observer mode is used to define event classes, listener classes, and publishing classes. At the same time, it needs to complete the function of a broadcaster. When receiving event push, it analyzes and processes events that meet the interests of listener event recipients, that is, it uses isAssignableFrom to judge.
  • IsAssignableFrom is similar to Instanceof, but isAssignableFrom is used to determine the relationship between a subclass and its parent, or between an interface’s implementation class and its interface. By default, the ultimate parent of all classes is Object. If a.isassignableFrom (B) is true, B can be converted to A, that is, A can be converted from B.

Four, implementation,

1. Engineering structure

small-spring-step-10└ ─ ─ the SRC ├ ─ ─ the main │ └ ─ ─ Java │ └ ─ ─ cn. Bugstack. Springframework │ ├ ─ ─ beans │ │ ├ ─ ─ factory │ │ │ ├ ─ ─ the config │ │ │ │ ├ ─ ─ AutowireCapableBeanFactory. Java │ │ │ │ ├ ─ ─ BeanDefinition. Java │ │ │ │ ├ ─ ─ spring 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 │ │ │ ├ ─ ─ the BeanFactory. Java │ │ │ ├ ─ ─ BeanFactoryAware. Java │ │ │ ├ ─ ─ BeanNameAware. Java │ │ │ ├ ─ ─ ConfigurableListableBeanFactory. Java │ │ │ ├ ─ ─ DisposableBean. Java │ │ │ ├ ─ ─ FactoryBean. Java │ │ │ ├ ─ ─ HierarchicalBeanFactory. Java │ │ │ ├ ─ ─ InitializingBean. Java │ │ │ └ ─ ─ ListableBeanFactory. Java │ │ ├ ─ ─ BeansException. Java │ │ ├ ─ ─ PropertyValue. Java │ │ └ ─ ─ PropertyValues. Java │ ├ ─ ─ the 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 │ │ ├ ─ ─ the Resource. The Java │ │ ├ ─ ─ ResourceLoader. Java │ │ └ ─ ─ UrlResource. Java │ └ ─ ─ utils │ └ ─ ─ ClassUtils. Java └ ─ ─ the test └ ─ ─ Java └ ─ ─ cn. Bugstack. Springframework. Test ├ ─ ─ event │ ├ ─ ─ ContextClosedEventListener. Java │ ├ ─ ─ ContextRefreshedEventListener. Java │ ├ ─ ─ CustomEvent. Java │ └ ─ ─ CustomEventListener. Java └ ─ ─ ApiTest. JavaCopy the code

Project source: public account “bugStack wormhole stack”, reply: Spring column, get the full source code

Container events and event listeners implement class relationships, as shown in Figure 11-2

  • More than the whole class diagram to surrounding the event event definition, distribution, monitoring functions, and the related content of the event using AbstractApplicationContext# refresh for registration and processing operations.
  • In the implementation process, we mainly extend the Spring Context package, and the implementation of events is also extended under this package. Of course, we can also see that all the current implementation content is still based on IOC.
  • ApplicationContext container inheritance ApplicationEventPublisher event publishing function interface, and provide event listeners in the implementation class functions.
  • ApplicationEventMulticaster interface is registered listeners and publish event broadcast, provide methods to add, remove, and release events.
  • Finally released container closing event, this still needs to be extended to AbstractApplicationContext# close approach, by the registered with the virtual machine implementation of hook.

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); }}Copy the code
  • An abstract class ApplicationEvent with event functionality is defined from java.util.EventObject, which all subsequent event classes need to inherit from.

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 ApplicationContext that the event was raised for. */
    public final ApplicationContext getApplicationContext(a) {
        return(ApplicationContext) getSource(); }}Copy the code

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); }}Copy the code

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); }}Copy the code
  • ApplicationContextEvent is an abstract class that defines events. All events, including close, refresh, and user-implemented events, need to inherit from this class.
  • ContextClosedEvent and ContextRefreshedEvent, respectively, are two event classes implemented by the Spring framework and can be used to listen for 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);

}
Copy the code
  • The event broadcaster defines methods to add and remove listeners and a method to broadcast eventsmulticastEventThe final push time message also passes through this interface method to handle 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;
    }

    /** * whether the listener is interested in the event */
    protected boolean supportsEvent(ApplicationListener<ApplicationEvent> applicationListener, ApplicationEvent event) {
        Class<? extends ApplicationListener> listenerClass = applicationListener.getClass();

        / / in accordance with the CglibSubclassingInstantiationStrategy, SimpleInstantiationStrategy different instantiate the type, you need to determine after obtaining the target classClass<? > 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);
        }
        // Determine whether the class or interface represented by this eventClassName object is the same as the class or interface represented by the specified event.getClass() argument, or whether it is its superclass or superinterface.
        // isAssignableFrom is used to determine the relationship between a subclass and its parent, or the interface implementation class and the interface. By default, all classes have an Object parent. If a.isassignableFrom (B) is true, B can be converted to A, that is, A can be converted from B.
        returneventClassName.isAssignableFrom(event.getClass()); }}Copy the code
  • AbstractApplicationEventMulticaster is the common method of extraction of event broadcast, can achieve some basic functions in this class, avoid all lots directly implement interface need to deal with the details.
  • Such as addApplicationListener, removeApplicationListener, in addition to the general method, here in this class is mainly to getApplicationListeners and supportsEvent processing.
  • The getApplicationListeners method extracts listeners that match broadcast events. The specific filtering actions are provided in the supportsEvent method.
  • In the supportsEvent method, the target Class needs to be obtained for different instantiations of Cglib and Simple. The Cglib proxy Class needs to obtain the Class of its parent Class, but common instantiations do not. ParameterizedType and eventClassName are the interfaces and corresponding ParameterizedType and eventClassName to verify whether the event is a subclass or a parent class.Refer to the comments in the code

SupportsEvent Method running screenshot

  • As you can see from code debugging, eventClassName and Event.getClass () are finally true by isAssignableFrom
  • About CglibSubclassingInstantiationStrategy, SimpleInstantiationStrategy can try AbstractApplicationContext class change the validation.

4. Definition and implementation of event publishers

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);

}
Copy the code
  • ApplicationEventPublisher is the release of the event interface, all events that need to be released from this interface go out.

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(a) throws BeansException {

        // 6. Initialize event publishers
        initApplicationEventMulticaster();

        // 7. Register event listeners
        registerListeners();

        // 9. Publish the container refresh completion event
        finishRefresh();
    }

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

    private void registerListeners(a) {
        Collection<ApplicationListener> applicationListeners = getBeansOfType(ApplicationListener.class).values();
        for(ApplicationListener listener : applicationListeners) { applicationEventMulticaster.addApplicationListener(listener); }}private void finishRefresh(a) {
        publishEvent(new ContextRefreshedEvent(this));
    }

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

    @Override
    public void close(a) {
        // Publish the container closure event
        publishEvent(new ContextClosedEvent(this));

        // Execute the destruction method that destroys the singleton beangetBeanFactory().destroySingletons(); }}Copy the code
  • In the abstract application context AbstractApplicationContext# refresh, mainly addedInitialize the event publisher,Register event listeners,Publish the container refresh completion eventThree methods are used to handle event operations.
  • Initialize the event publisher (initApplicationEventMulticaster), is mainly used to instantiate a SimpleApplicationEventMulticaster, this is an event broadcast.
  • Register event listeners to get all the event configuration Bean objects loaded from spring.xml via the getBeansOfType method.
  • Publish container finishRefresh, which publishes the event after the first server has been started, published via publishEvent, Actually is also called the applicationEventMulticaster. MulticastEvent (event); Methods.
  • Finally, in a close method, we added the publishing of a container close event.publishEvent(new ContextClosedEvent(this));

Five, the 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
}
Copy the code
  • Create a custom event that can add its own desired entry information to the constructor of the event class. The event class will eventually be finished in the listener, so any properties you add will be retrieved.

cn.bugstack.springframework.test.event.CustomEventListener

public class CustomEventListener implements ApplicationListener<CustomEvent> {

    @Override
    public void onApplicationEvent(CustomEvent event) {
        System.out.println("Received:" + event.getSource() + "The message; Time:" + new Date());
        System.out.println("News:" + event.getId() + ":"+ event.getMessage()); }}Copy the code
  • This is a listener that listens for CustomEvent events, where you can do whatever you want, such as sending coupons and SMS notifications after some users register.
  • The other thing is aboutContextRefreshedEventListener implements ApplicationListener<ContextRefreshedEvent>,ContextClosedEventListener implements ApplicationListener<ContextClosedEvent>Listener, here will not demonstrate, can refer to the source code.

2. Configuration file


      
<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>
Copy the code
  • Three event listeners are configured in spring.xml to listen for refresh, custom events, and close events.

Unit testing

public class ApiTest {

    @Test
    public void test_event(a) {
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
        applicationContext.publishEvent(new CustomEvent(applicationContext, 1019129009086763L."It worked!)); applicationContext.registerShutdownHook(); }}Copy the code
  • By using applicationContext’s new publish event interface method, publish a CustomEvent, CustomEvent, and pass through the parameters.

The test results

Refresh event: cn bugstack. Springframework. Test. The event. The ContextRefreshedEventListener $$EnhancerByCGLIB $$440 a36f5 received: Cn. Bugstack. Springframework. Context. Support. ClassPathXmlApplicationContext @ 71 c7db30 message; Time:22:32:50Message:1019129009086763: It worked! Closing event: cn.bugstack.springframework.test.event.ContextClosedEventListener$$EnhancerByCGLIB$$f4d4b18d Process finished with exit code0
Copy the code
  • From the test results, we can see that the events and listeners defined by ourselves, as well as the event information of the listening system, can be completely output in the console. You can also try adding some other event behavior and debugging code to learn observer mode.

Six, summarized

  • Throughout the learning process of the Handwritten Spring framework, you can gradually see the use of many design patterns, such as: simple factory BeanFactory, factory method FactoryBean, policy pattern access to resources, and now implement the specific use of an observer pattern. Therefore, in the process of learning Spring, you should pay more attention to the application of design patterns, which is the core of your ability to understand the code and the focus of learning.
  • So this section of the observer pattern implementation process, mainly including the definition of events, surveillance and release events, release is completed according to the matching strategy, the listener will receive their own event content, and make corresponding processing movement, the observer pattern actually everyday we also often use, but after the combination of Spring, In addition to learning design patterns, you can also learn how to integrate the implementation of the corresponding observer with the application context.
  • All the technologies, designs and ideas learned in Spring can be combined with the actual business development, and these seemingly many code modules are actually expanded step by step according to their respective responsibilities. In the process of learning, you can first try to complete these framework functions, in a bit of debugging way with Spring source code reference, and eventually slowly master these design and coding capabilities.

Seven, series recommendation

  • Learn Spring, start with design mode!
  • Middleware learning deeply associated with Spring
  • How many lines of code do I have to write after college to get a development job without spending money on training?
  • On Spring source code getBean full process source parsing
  • Based on JDBC to achieve a Demo version of Mybatis