The Observer pattern plays an important role in the Java language. The Java.util package in the JDK provides the Observable class and the Observer interface that make up the JDK’s support for the Observer pattern. However, in Java9 it was deprecated. In addition, we use the Spring framework extensively in JAVA applications, and spring also provides an implementation of the Observer pattern. The Spring event-driven model is also a classic application of the Observer pattern. Is the most common event listener in our common project.

Theory suggests

Start by describing the roles defined in Spring to implement the Observer pattern:

  1. ApplicationEvent, which is the parent class of all event objects. ApplicationEvent inherits from EventObject in the JDK. All events need to inherit ApplicationEvent and get the event source from source. You can dig a little deeper into Spring, which gives us a lot of built-in events to extend, for example
  • ContextStartedEvent: Spring Context startup completion event.
  • ContextStoppedEvent: Spring Context stops the completion event.
  • ContextClosedEvent: Spring Context stops the start event.
  • ContextRefreshedEvent: Spring Context initialization or refresh completion event.
  1. ApplicationListener, or observer, inherits from the JDK’s EventListener, which has only one method, onApplicationEvent. This method is executed when a listening event occurs.
  2. ApplicationContext. ApplicationContext is the core container in Spring and can act as the publisher of events in event listeners, that is, the event source. Because the ApplicationContext inherited from ApplicationEventPublisher. Defines the method of event publishing: in ApplicationEventPublisher publishEvent (Object event)
  3. ApplicationEventMulticaster (events), used for event listener registration and broadcasting of events. It is used to register listeners by broadcasting events published by Applicationcontext to its list of listeners.

Comparison in the scene

Taking a business scenario as an example, when pushing and publishing tasks, control classes of another three business layers need to be injected into the business layer and corresponding business processes are carried out afterwards. As shown in the figure below, it can be seen that the coupling between businesses is very serious.

Based on the above process, let’s use the observer mode to transform:

A big part of the observer pattern for us is business decoupling. Take the simple scenario of user registration as an example. Suppose that when user registration is completed, it is necessary to send emails and coupons to the user, as shown in the following figure:

So let’s take an example based on this scenario:

  • We build a SpringBoot project that introduces dependencies:
<? xml version="1.0" encoding="UTF-8"? > <project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.22..RELEASE</version> <relativePath/> <! -- lookup parent from repository --> </parent> <modelVersion>4.0. 0</modelVersion>

    <artifactId>lab-54-demo</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

</project>

Copy the code
  • Define an event (in this case, a user registration event)
package com.springboot.original.service;

import org.springframework.context.ApplicationEvent;

public class UserRegisterEvent extends ApplicationEvent {

    private String username;

    public UserRegisterEvent(Object source) {
        super(source);
    }
    public UserRegisterEvent(Object source, String username) {
        super(source);
        this.username = username;
    }

    public String getUsername(a) {
        returnusername; }}Copy the code
  • Define a concrete business implementation class, UserService
@Service
public class UserService implements ApplicationEventPublisherAware {
    private Logger log = LoggerFactory.getLogger(this.getClass());
    @Autowired
    private UserMapper userMapper;
    @Resource
    RedisUtil redisUtil;

    private ApplicationEventPublisher applicationEventPublisher;

    public User selectByPrimaryKey(String id){
        return userMapper.selectByPrimaryKey(id);
    }

    public int insertUser(User user){
        return userMapper.insert(user);
    }

    public User selectByNickName(String name){
        redisUtil.set("aaa"."bbb");
        return userMapper.selectByNickName(name);
    }

    public String getFromRedis(String key){
        return redisUtil.get(key)+"";
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    public void register(String username) {
        // Register logic
        System.out.println("[register][performs registration logic for user ({})]"+ username);

        // Publish the message
        applicationEventPublisher.publishEvent(new UserRegisterEvent(this, username)); }}Copy the code
  • EmailService defines EmailService to monitor the service processing of sending emails after publishing messages
package com.springboot.original.service;

import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class EmailService implements ApplicationListener<UserRegisterEvent> {

    @Override
    @Async
    public void onApplicationEvent(UserRegisterEvent event) { 
        System.out.println("[onApplicationEvent][Sends mail to user ({})]"+ event.getUsername()); }}Copy the code

To illustrate the purpose of this class, this class implements the ApplicationListener interface and sets the events of interest through generics. Set this event to UserRegisterEvent. Custom processing is then performed for the listening UserRegisterEvent event.

  • Let’s define a coupon Service
package com.springboot.original.service;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;

@Service
public class CouponService {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @EventListener
    public void addCoupon(UserRegisterEvent event) {
        logger.info([addCoupon][give coupon to user ({})], event.getUsername()); }}Copy the code

Note that the method in this class adds the @EventListener annotation, which is just another way of writing it, and essentially means the same thing as the service that sends mail, by adding the annotation to indicate that the method is listening for the event as UserRegisterEvent.

  • Let’s test that calling the normal registration method from controller triggers the corresponding mail listening event and coupon listening event

Sequence control of each monitor

If there are multiple business implementation classes listening for the same event and sequence control is required in different business implementation classes, then we have two cases:

  1. Using annotations to listen, the ORDER annotation should be configured on the method that marks the annotation

  1. When the implementation class implements its methods for listening, the Order annotation is configured on the business implementation class

The smaller the @order annotation value, the more it is loaded first

Observer versus publish and subscribe

For the Observer mode, we only need to maintain one Observable, that is, an Observable instance. When data changes, it only needs to maintain a set of observers. These observers implement the same interface. Notifying the Observer of which unified method needs to be invoked. The diagram below:

More like an MQ middleware for the publish-subscribe pattern

Unlike the observer model, in the publish-subscribe model, publishers and subscribers are not loosely coupled, but completely decoupled. In the Observer mode, there are only two roles — observer + observed, whereas in the publish-subscribe mode, there are not only two roles of publisher and subscriber, but also a “middleman” who manages and executes message queues.