Spring事件机制:ApplicationEvent

Spring事件机制:ApplicationEvent

1. 事件机制

23种设计模式中的观察者模式,主要有观察者和被观察者两个角色,是一种对象间一对多依赖关系的实现,在软件设计中也被广泛使用。

Spring的事件机制也是通过这个模式驱动的,事件发布者是被观察者,事件中的监听者则是观察者,以 发布-订阅 模式实现。

2. 事件驱动

在跨进程之间,通常会采用 MQ(消息队列)来实现消息的发布和订阅,从而进行通信;在同一进程内,很多时候也可以使用事件驱动机制来进行逻辑和功能上的解耦,常见的比如注册、订单等。

public void register(){
    // 保存用户信息
    doSaveUserIno();
    // 发送邮件通知
    doEmail();   
}

用户注册流程有验证用户信息、保存用户信息、邮件提醒注册成功,如果想要在注册的时候添加别的流程,例如将用户信息存储到其他地方,就需要去改动这边的逻辑实现;其他地方想要复用,必然要重新封装到新的类中,虽然有些情况下改动量较少,但是对代码的复用性和可读性、扩展性都不佳。

在这种情况下,很多人都会想到:将功能模块化,提出公共的方法,使用的时候去调用,这是一种常见的优化思路。在此基础上,通过事件发布-订阅机制,可以大大增加该场景下代码的扩展性和复用性。

3. 事件机制源码概述

事件机制主要有三部分:事件源、事件对象、监听器。

事件源:事件发生的起源,通常是处理业务流程中

事件对象:事件实体,也记录了事件源

监听器:监听事件对象,对事件对象进行处理

3.1 Java定义的事件机制

Java中定义了事件机制的两个顶层类:EventObject ,事件的顶层父类(实现了Serializable接口);

EventListener,定义了监听器的顶层接口

3.1.1 EventObject:事件
public class EventObject implements java.io.Serializable {

    private static final long serialVersionUID = 5516075349620653480L;

    /**
     * The object on which the Event initially occurred.
     */
    protected transient Object  source;

    /**
     * Constructs a prototypical Event.
     *
     * @param    source    The object on which the Event initially occurred.
     * @exception  IllegalArgumentException  if source is null.
     */
    public EventObject(Object source) {
        if (source == null)
            throw new IllegalArgumentException("null source");

        this.source = source;
    }

    /**
     * The object on which the Event initially occurred.
     *
     * @return   The object on which the Event initially occurred.
     */
    public Object getSource() {
        return source;
    }

    /**
     * Returns a String representation of this EventObject.
     *
     * @return  A a String representation of this EventObject.
     */
    public String toString() {
        return getClass().getName() + "[source=" + source + "]";
    }
}
3.1.2 EventListener:监听者
/**
 * A tagging interface that all event listener interfaces must extend.
 * @since JDK1.1
 */
public interface EventListener {
}

3.2 Spring事件使用

Spring 中也对Java中事件机制做了很多的扩展和衍生,提供了很多方便的接口和类。

对事件的定义如下:

ApplicationEvevnt:继承 EventObject 类,自定义事件源应该继承该类

ApplicationEventListener:继承EventListener接口,自定义监听者可以通过实现该接口

ApplicationEventPublisher :封装了事件发布的方法,通知所有在 Spring 中注册的监听者进行处理

基于Spring提供的基类,可以进行自定义各类符合业务和流程的事件;自定义的监听者实现类,可以由 Spring 容器进行管理,只需要通过 ApplicationEventPublisher 进行发布进行,不用自己去实现监听者的注册、通知等等过程。

3.2.1 ApplicationEvevnt:事件
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 {@code ApplicationEvent}.
	 * @param source the object on which the event initially occurred or with
	 * which the event is associated (never {@code null})
	 */
	public ApplicationEvent(Object source) {
		super(source);
		this.timestamp = System.currentTimeMillis();
	}

	/**
	 * Return the system time in milliseconds when the event occurred.
	 */
	public final long getTimestamp() {
		return this.timestamp;
	}
}
3.2.2 ApplicationEventListener:监听者
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

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

	/**
	 * Create a new {@code ApplicationListener} for the given payload consumer.
	 * @param consumer the event payload consumer
	 * @param  the type of the event payload
	 * @return a corresponding {@code ApplicationListener} instance
	 * @since 5.3
	 * @see PayloadApplicationEvent
	 */
	static <T> ApplicationListener<PayloadApplicationEvent<T>> forPayload(Consumer<T> consumer) {
		return event -> consumer.accept(event.getPayload());
	}

}
3.2.3 ApplicationEventPublisher: 事件发布
@FunctionalInterface
public interface ApplicationEventPublisher {

	default void publishEvent(ApplicationEvent event) {
		publishEvent((Object) event);
	}

	void publishEvent(Object event);

}

4. 事件机制的使用

4.1 自定义事件

  • 继承 ApplicationEvent

  • 可以在Event中自定义类属性

    原生的 ApplicationEvent事件中,有属性 timestamp 时间戳,用来标识事件发生的时间,我们可以自己定义事件中的属性,将一些消息传递给监听者处理的时候使用。

    下面案例中,我定义了一个 User 类,用来记录用户注册时的信息:用户名、密码等等

public class UserRegisterEvent extends ApplicationEvent {

    /**
     * serial Version UID
     */
    private static final long serialVersionUID = -5058858385060733297L;

    /** 
     *  Custom entity 
     */
    private final User user;

    /**
     * Create a new {@code ApplicationEvent}.
     *
     * @param source the object on which the event initially occurred or with
     *               which the event is associated (never {@code null})
     */
    public UserRegisterEvent(Object source, User user) {
        super(source);
        this.user = user;
    }

    public User getUser() {
        return user;
    }
}

@Getter
@Setter
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class User implements Serializable {

    private static final long serialVersionUID = -8967972309006473679L;
    /**
     * 用户名
     */
    private String username;
    /**
     * 密码
     */
    private String password;
    
    // 等等注册信息
}

4.2 自定义监听者

定义监听者,对指定的事件进行处理,只要在监听者内容的处理代码不同,就可以对一个事件的多个业务进行处理。

4.2.1 实现 ApplicationListener 接口

通过实现 ApplicationListener 接口来实现一个事件监听者,ApplicationListener 接口可以通过泛型事件的传入,来实现对指定事件的监听;通过重写 onApplicationEvent(E event) 方法来实现对事件具体的业务处理

// 监听 UserRegisterEvent事件
@Component
@Slf4j
@Order(0)
public class RegisterListener implements ApplicationListener<UserRegisterEvent> {
    

    /**
     * 使用接口实现的方式来监听事件
     *
     * @param event 用户注册事件
     */
    @Override
    public void onApplicationEvent(UserRegisterEvent event) {

        User user = event.getUser();

        // do something

        log.info("ApplicationLister注册信息,用户名:" + user.getUsername() + ",密码:" + user.getPassword());

    }
}
4.2.2 使用 @EventListener注解

使用该注解,可以将类中的某个方法标记为监听者,参数为需要监听的事件

@Component
@Slf4j
public class AnnotationRegisterListener {

    /**
     * 使用注解{@linkplain EventListener}的方式来监听用户注册事件 UserRegisterEvent
     * {@linkplain Order}来标记同步时的执行顺序
     *
     * @param userRegisterEvent 用户注册事件
     */
    @EventListener
    @Order(value = 3)
    public void register(UserRegisterEvent userRegisterEvent) {

        // get register user
        User user = userRegisterEvent.getUser();

        // do something

        log.info("@EventListener注册信息,用户名:" + user.getUsername() + ",密码:" + user.getPassword());
    }
}
4.2.3 实现SmartApplicationListener 接口

该接口是继承了 ApplicationListenerOrdered 的子类,可以监听所有的事件,通过对支持的事件源(supportsSourceType)和支持的事件(supportsEventType)进行校验来实现对指定事件的监听,通过定义 getOrder() 方法来指定该监听者处理事件的执行顺序;具体的处理代码也是在 onApplicationEvent(E event) 中实现。

@Component
@Slf4j
public class UserRegisterListener implements SmartApplicationListener {

    /**
     * Determine whether this listener actually supports the given event type.
     *
     * @param eventType the event type (never {@code null})
     */
    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        // support UserRegisterEvent.class
        return eventType == UserRegisterEvent.class;
    }

    /**
     * Determine whether this listener actually supports the given source type.
     * 

The default implementation always returns {@code true}. * * @param sourceType the source type, or {@code null} if no source */ @Override public boolean supportsSourceType(Class<?> sourceType) { // support UserServiceImpl which publish event return sourceType == UserServiceImpl.class; } /** * Determine this listener's order in a set of listeners for the same event. *

The default implementation returns {@link #LOWEST_PRECEDENCE}. */ @Override public int getOrder() { // 0 is highest priority return 0; } @Override public String getListenerId() { return null; } /** * Handle an application event. * * @param event the event to respond to */ @Override public void onApplicationEvent(ApplicationEvent event) { // change event type into target event UserRegisterEvent userRegisterEvent = (UserRegisterEvent) event; // get user User user = userRegisterEvent.getUser(); // do something // print log.info("注册监听器001:执行顺序:" + getOrder()); log.info("注册信息,用户名:" + user.getUsername() + ",密码:" + user.getPassword()); } }

4.3 发布事件

完成了事件的自定义和监听者的注册,下面就是发布事件。

Spring 中的事件发布比较简单,可以使用顶层接口 ApplicationEventPublisher 进行发布,也可以使用 Spring 上下文 ApplicationContext 进行发布。

@Resource
private ApplicationContext applicationContext;

@Resource
private ApplicationEventPublisher publisher;

public void register(User user) {
    if (Objects.isNull(user)) {
        log.error("User is null.");
        return;
    }

    if (StringUtils.isEmpty(user.getUsername()) || StringUtils.isEmpty(user.getPassword())) {
        log.error("User name and password is null.");
        return;
    }

    // do something

    // 使用 Spring 上下文 ApplicationContext 发布事件
    applicationContext.publishEvent(new UserRegisterEvent(this, user));
    
    // 使用 ApplicationEventPublisher 发布事件
    publisher.publishEvent(new UserRegisterEvent(this, user));
}

你可能感兴趣的:(Java,java)