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
接口
该接口是继承了 ApplicationListener
、Ordered
的子类,可以监听所有的事件,通过对支持的事件源(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));
}