spring-boot-2.0.3 External story of different series - spring boot event mechanism, absolutely worth your attention

Keywords: Java Spring JDK SpringBoot

Preface

This article was meant to continue discussing the springboot startup source with you, but it seems that no one has the courage to look at it anymore. Today, instead of talking about the springboot startup source, let's first look at an interesting topic. What's more, you should already know, that's the spring-boot event in the title.

Maybe it's strange for your little buddy to say nothing about the good source series. Why do you suddenly say something that doesn't matter?So what really matters?There will be an answer to this question later.Now you all have the right to relax and look down with me in a relaxed way.

Observer mode

Didn't you say springboot's event mechanism? How can you say what observer mode?In my heart, I would say, "Are you okay, landlord? Are you clipped by the door today?"Lower: "Door clip?It doesn't exist. Usually it's just me clipping the door..."

Then, everyone relaxes and looks slowly, keeping their questions in mind or taking notes, and then they will be slowly released.

The concept is really simple: two subjects, one observer and one observer, when the observee changes, the observer will behave accordingly.For example, a traffic light that is closely related to our daily life is equivalent to an observer, and a pedestrian is equivalent to an observer. When the light changes, pedestrians will behave accordingly: the red light stops, the green light goes on, the yellow light goes on and so on.Another example is the public number we're playing with right now. When we subscribe to a public number, every time it publishes an article, it sends it to the subscribers, so we can browse through it; when we unsubscribe, it doesn't push us anymore; as long as it's running, people will always subscribe to it orCancel the subscription.The two subjects have a unified name: the observer becomes the Subject, and the observer is still called the Observer.

There are many other terms for observer mode, such as Publish/Subscribe mode, Model/View mode, Source/Listener mode, or Dependents mode.The observer mode defines a one-to-many dependency in which multiple observer objects listen to a subject object at the same time.When this subject object changes state, all observer objects are notified so that they can automatically update themselves.

What is said in theory is only in theory, so let's realize what the observer mode is.

Class Diagram

The roles involved are as follows:

Subject: Provides an interface to add and remove observer objects.Generally implemented with abstract classes or interfaces.
Abstract Observer: Provides an interface to update yourself when notified of a subject.Generally implemented with abstract classes or interfaces.
ConcreteSubject: Store the relevant state in the specific observer and notify all registered observers when the internal state of the specific subject changes.Generally, the implementation is a concrete subclass.
ConcreteObserver: The state in which the storage and theme are in place.Specific observer roles implement the update interface required by abstract observer roles to coordinate their own state with that of the subject.A specific observer role can maintain a reference to a specific subject object if needed

In the above class diagram, ConcreteSubject has a list of stored Observers, which means that ConcreteSubject does not need to know which ConcreteObserver s are referenced, and objects that implement (inherit) Observers can be stored in the list.Call Observer's update method when needed.

- General implementation

    Subject:

package com.lee.myobserver;


/**
 * Abstract Theme
 * Provides interfaces to be implemented for specific topics
 */
public interface Subject {

    /**
     * Register Observers
     * @param observer
     */
    void attach(Observer observer);

    /**
     *  Remove Observer
     *  @param observer
     */
    void detach(Observer observer);

    /**
     * Notify all registered observers
     */
    void notifyObservers();
}

    Observer:

package com.lee.myobserver;

/**
 * Abstract observer
 * Provide interfaces that specific observers need to implement
 */
public interface Observer {

    /**
     *
     */
    void update(String state);
}

    ConcreteSubject:

package com.lee.myobserver.impl;

import com.lee.myobserver.Observer;
import com.lee.myobserver.Subject;

import java.util.ArrayList;
import java.util.List;

/**
 * Specific topics
 */
public class ConcreteSubject implements Subject {

    private List<Observer> observerList = new ArrayList<>();
    private String state;

    @Override
    public void attach(Observer observer) {
        this.observerList.add(observer);
        System.out.println("towards ConcreteSubject Registered an observer");
    }

    @Override
    public void detach(Observer observer) {
        this.observerList.remove(observer);
        System.out.println("from ConcreteSubject Remove an observer");
    }

    @Override
    public void notifyObservers() {
        this.observerList.forEach(observer -> observer.update(this.state));
    }

    public void changeState(String state) {
        this.state = state;
        this.notifyObservers();
    }
}

    ConcreteObserver:

package com.lee.myobserver.impl;

import com.lee.myobserver.Observer;

/**
 * Specific observer
 */
public class ConcreteObserver implements Observer {

    @Override
    public void update(String state) {
        System.out.println("I've been notified that I want to change my status to:" + state);
    }
}

    MyObserverTest:

package com.lee.test;

import com.lee.myobserver.Observer;
import com.lee.myobserver.impl.ConcreteObserver;
import com.lee.myobserver.impl.ConcreteSubject;

public class MyObserverTest {

    public static void main(String[] args) {
        ConcreteSubject subject = new ConcreteSubject();
        Observer observer = new ConcreteObserver();
        subject.attach(observer);
        subject.changeState("new state");
    }
}

Complete code can be obtained from spring-boot-test Get it, under com.lee.myobserver

The results are as follows:

In the above implementation, we found that the ConcreteSubject must maintain an Observer list, which raises the question: Will the List <Observer>in each ConcreteSubject be different?Obviously, no, because the type that List saves is the interface type, can we put this maintenance list in the Subject?We also found that attach, detach, notifyObservers are implemented the same in each ConcreteSubject, so can we share them?The answer is yes!So what do we do? Just change the Subject to an abstract class. The class diagram is as follows. The specific implementation is left to you. With this class diagram, we believe that everyone can easily complete the implementation of the code.

Implementation of jdk

Under the Java language's java.util package, there is an Observable class and an Observer interface that make up the Java language's support for observer mode.

    Observable:

package java.util;

/**
 * Abstract theme, implemented with common classes
 */
public class Observable {
    private boolean changed = false;
    private Vector<Observer> obs;

    
    /**
     * Build a theme with 0 observers
     */
    public Observable() {
        obs = new Vector<>();
    }

    /**
     * Register an observer in obs
     */
    public synchronized void addObserver(Observer o) {
        if (o == null)
            throw new NullPointerException();
        if (!obs.contains(o)) {
            obs.addElement(o);
        }
    }

    /**
     * Remove an observer from obs
     */
    public synchronized void deleteObserver(Observer o) {
        obs.removeElement(o);
    }

    /**
     * Equivalent to notifyObservers(null), see the one below
     */
    public void notifyObservers() {
        notifyObservers(null);
    }

    /**
     * If this object changes, notify all registered observers and call their update method
     */
    public void notifyObservers(Object arg) {
        /*
         * a temporary array buffer, used as a snapshot of the state of
         * current Observers.
         */
        Object[] arrLocal;

        synchronized (this) {
            if (!changed)
                return;
            arrLocal = obs.toArray();
            clearChanged();
        }

        for (int i = arrLocal.length-1; i>=0; i--)
            ((Observer)arrLocal[i]).update(this, arg);
    }

    /**
     * Empty obs
     */
    public synchronized void deleteObservers() {
        obs.removeAllElements();
    }

    /**
     * Set changed to true to indicate that the object has changed
     */
    protected synchronized void setChanged() {
        changed = true;
    }

    /**
     * Reset changed to false
     */
    protected synchronized void clearChanged() {
        changed = false;
    }

    /**
     * Detect if this object has changed
     */
    public synchronized boolean hasChanged() {
        return changed;
    }

    /**
     * Return number of registered observers
     */
    public synchronized int countObservers() {
        return obs.size();
    }
}

    Observer:

package java.util;

/**
 * Abstract observer, interface implementation
 */
public interface Observer {
    /**
     * This method is called when the observee object changes
     */
    void update(Observable o, Object arg);
}

    Watched:

package com.lee.jdkobserver;

import java.util.Observable;

/**
 * Specific topics
 */
public class Watched extends Observable {
    private String data = "";

    public void changeData(String data) {
        if (this.data.equals(data)) {
            return;
        }
        this.data = data;
        setChanged();
        notifyObservers(this.data);
    }
}

    Watcher:

package com.lee.jdkobserver;

import java.util.Observable;
import java.util.Observer;

/**
 * Specific observer, implementation of Observer in jdk
 */
public class Watcher implements Observer {

    @Override
    public void update(Observable o, Object arg) {
        System.out.println("The data changed to:" + arg);
    }
}

    JdkObserverTest:

package com.lee.test;

import com.lee.jdkobserver.Watched;
import com.lee.jdkobserver.Watcher;

import java.util.Observer;

public class JdkObserverTest {

    public static void main(String[] args) {
        Watched watched = new Watched();
        Observer observer = new Watcher();
        watched.addObserver(observer);
        watched.changeData("first");
        watched.changeData("second");
        watched.changeData("third");
        watched.changeData("fourth");
    }
}

Complete code can be obtained from spring-boot-test Get it, under com.lee.jdkobserver

The results are as follows

The class diagram is as follows

jdk event

JDK 1.0 and earlier versions of the event model are based on the responsibility chain model, but this model is not applicable to complex systems, so in JDK 1.1 and later versions, the event processing model uses the Delegation Event Model (DEM) based on the observer mode, which means that events raised by a Java component are not handled by the object that caused the event.Instead, delegate responsibility to independent event-handling objects.This is not to say that event models are based on Observer and Observable. Event models have nothing to do with Observer and Observable. Observer and Observable are just one implementation of Observer mode.

Participants in event mechanisms in java have three roles

Event Eource: The source of the event, the subject that initiated the event.

Event Object: An event state object, the information carrier that is passed, is like a parameter of Watcher's update method, which can be the event source itself and generally exists in the listerner's method as a parameter.

Event Listener: An event listener that, when it listens for an event object to be generated, calls the appropriate method to process it.

There is one more thing that matters: an event environment in which event listeners can be added, events can be generated, and event listeners can be triggered.

Limited to space, the implementation of specific cases will not be discussed, you can go spring-boot-test Get, under com.lee.jdkevent, the notes are written in detail, so you can have a good look.

spring Event Mechanism

Springboot does not have its own event mechanism. It uses Spring's event mechanism. I hope you don't think it's inconsistent with the title here.The relationship between springboot and spring can be touched by everyone. Here we can tell you clearly that it is not an antagonistic relationship!

spring's event mechanism is also an extension of java's. Look further

All event parent interfaces in ApplicationEvent:Spring, inherited from EventObject in java

/*
 * Copyright 2002-2015 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context;

import java.util.EventObject;

/**
 * Class to be extended by all application events. Abstract as it
 * doesn't make sense for generic events to be published directly.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 */
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 ApplicationEvent.
     * @param source the object on which the event initially occurred (never {@code null})
     */
    public ApplicationEvent(Object source) {
        super(source);
        this.timestamp = System.currentTimeMillis();
    }


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

}

All event listener parent interfaces in ApplicationListener:spring, inherited from java's EventListener

/*
 * Copyright 2002-2016 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.context;

import java.util.EventListener;

/**
 * Interface to be implemented by application event listeners.
 * Based on the standard {@code java.util.EventListener} interface
 * for the Observer design pattern.
 *
 * <p>As of Spring 3.0, an ApplicationListener can generically declare the event type
 * that it is interested in. When registered with a Spring ApplicationContext, events
 * will be filtered accordingly, with the listener getting invoked for matching event
 * objects only.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @param <E> the specific ApplicationEvent subclass to listen to
 * @see org.springframework.context.event.ApplicationEventMulticaster
 */
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

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

}

- Specific cases

    MessageEvent:   

package com.lee.springevent.event;

import org.springframework.context.ApplicationEvent;

/**
 * SMS Events, Carrier of Event Information
 * You can get event source information from it, and in this case, source does not represent an event source
 */
public class MessageEvent extends ApplicationEvent {

    public MessageEvent(Object source) {
        super(source);
    }
}

    MessageListener:

package com.lee.springevent.listener;

import com.lee.springevent.event.MessageEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.ApplicationListener;

public class MessageListener implements ApplicationListener{

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if(event instanceof MessageEvent) {
            String phoneNumber = (String)event.getSource();
            System.out.println("I have been notified that we are about to"+phoneNumber+"Text message sent...");
        }
    }
}

    MessageService:

package com.lee.springevent.service;

import com.lee.springevent.event.MessageEvent;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

/**
 * ApplicationContextAware,Ability to get spring's context
 *
 * MessageService Equivalent to the aforementioned event environment
 */
public class MessageService implements ApplicationContextAware {

    private ApplicationContext ctx;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.ctx = applicationContext;
    }

    public void sendMessage(String phoneNumber)
    {
        MessageEvent evt = new MessageEvent(phoneNumber);

        // Publish Events
        ctx.publishEvent(evt);
    }
}

    spring-event.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="messageService" class="com.lee.springevent.service.MessageService" />
    <bean id="messageListener" class="com.lee.springevent.listener.MessageServiceListener" />

</beans>

    SpringEventTest:

package com.lee.test;

import com.lee.springevent.service.MessageService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class SpringEventTest {

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-event.xml");
        MessageService messageService = (MessageService) applicationContext.getBean("messageService");
        messageService.sendMessage("1574480311");
    }
}

You can see the results by executing the main method in SpringEventTest.

More details from spring-boot-test Get it. At com.lee.springevent, you can digest it up according to the notes.I really don't know what to say. I will focus on the source of spring's events in the next blog post together with you, which will also be mentioned in subsequent blogs.

summary

Why talk about something unrelated to springboot startup source

The question in the preamble: Why to tell a story that has nothing to do with the spring startup source? First, it really relaxes everyone and makes reading the source really tiring. Second, the main purpose is to prepare springboot for starting source 2. The next blog will cover the spring event mechanism

- Advantages and disadvantages of the observer model

The above lengthy discussion does not seem to discuss its advantages and disadvantages; in fact, you should be able to appreciate its advantages and disadvantages by realizing them. I conclude that there are some errors you can add to the commentary area

Strengths:

(1) Themes and observers establish an abstract coupling rather than a tight one, which reduces the coupling; themes only need to maintain a collection of abstract observers without knowing the specific observers, enabling a wide variety of different observer implementations.

(2) Broadcast communications are supported, and topics send notifications to all registered observers, simplifying the design of one-to-many systems.

(3) Compliance with the Open and Close Principles, the new specific observer can be expanded without modifying the original code.

Disadvantages:

(1) If the subject has many direct or indirect observers, it can be time consuming to notify all of them.

(2) A circular dependency between the subject and the observer may cause the system to crash.

- Scenarios for observer mode application

Abstract: Updating the state of an object requires synchronous updates of other objects, and the number of other objects is dynamically variable; an object only needs to notify other objects of its updates without knowing the details of other objects.You can use these two points as a basic guideline to determine whether a scene satisfies the observer mode.

There are many scenarios, such as event mechanisms, public number subscriptions, and tomcat sources (brothers who are interested can find them).

- Event mechanism

The implementation of jdk events is based on the observer model, while spring events are expanded on the basis of jdk events.

There are four main roles

Event source: The principal that triggers the event, such as UserService in the jdk event case, MessageService in the spring event case, SpringApplication in the SpringBoot.

Events: Events themselves refer to sources in EventObject s and can be any data (including event sources) used to transfer data, such as MessageEvents in jdk event cases and MessageEvents in spring event cases.

Event listener: Responsible for handling events when they occur, such as MessageListener in jdk event cases and MessageListener in spring event cases.

Incident context: The context in which the entire event is located, supporting the entire event, similar to the web context; for example, UserService in the jdk event case, ApplicationContext in the spring event case

Reference resources

Head First Design Mode

java and Modes

Posted by Zeradin on Fri, 10 May 2019 02:56:40 -0700