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 <T> 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.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<E extends ApplicationEvent> 接口可以通过泛型事件的传入,来实现对指定事件的监听;通过重写 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<UserRegisterEvent>注册信息,用户名:" + 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. * <p>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. * <p>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));
}
全部评论

相关推荐

不愿透露姓名的神秘牛友
11-24 20:55
阿里国际 Java工程师 2.7k*16.0
程序员猪皮:没有超过3k的,不太好选。春招再看看
点赞 评论 收藏
分享
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务