1.说明
Spring事件机制ApplicationEvent,
是一个典型的观察者模式,
包含三部分:
Event 事件(相当于消息)、
Publisher 发送者(相当于被观察者)、
Listener 监听者(相当于观察者)。
可以实现在Spring应用中,
通过发送者发布各种消息事件,
让监听者处理其关心的消息事件,
从而利用观察者模式解耦发送者和监听者。
2.事件
首先确定需要在事件中传递的消息,
比如下面的用户信息,
新建UserInfo.java:
package org.springframework.context.event.entity;
public class UserInfo {
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "UserInfo [name=" + name + ", age=" + age + "]";
}
}
然后创建事件类型,
比如用户的创建和删除事件,
分别创建UserAddEvent.java类:
package org.springframework.context.event.type;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.entity.UserInfo;
public class UserAddEvent extends ApplicationEvent {
private static final long serialVersionUID = -3910960183687086015L;
private UserInfo user;
public UserAddEvent(Object source, UserInfo user) {
super(source);
this.user = user;
}
public UserInfo getUser() {
return user;
}
public void setUser(UserInfo user) {
this.user = user;
}
@Override
public String toString() {
return "UserAddEvent [user=" + user + "]";
}
}
以及UserDelEvent.java类
package org.springframework.context.event.type;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.entity.UserInfo;
public class UserDelEvent extends ApplicationEvent {
private static final long serialVersionUID = -3910960183687086015L;
private UserInfo user;
public UserDelEvent(Object source, UserInfo user) {
super(source);
this.user = user;
}
public UserInfo getUser() {
return user;
}
public void setUser(UserInfo user) {
this.user = user;
}
@Override
public String toString() {
return "UserDeleteEvent [user=" + user + "]";
}
}
注意自定义的事件类,需要继承ApplicationEvent类,
在自定义类中承载需要发布的消息。
3.发送者
发送者使用ApplicationContext的publishEvent方法发布用户事件,
只需要在发布事件的地方注入ApplicationContext实例即可:
package org.springframework.context.event.publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.event.type.UserAddEvent;
import org.springframework.context.event.type.UserDelEvent;
import org.springframework.stereotype.Component;
@Component
public class UserEventPublisherContext {
@Autowired
private ApplicationContext applicationContex;
public void publishUserAddEvent(UserAddEvent event) {
applicationContex.publishEvent(event);
}
public void publishUserDelEvent(UserDelEvent event) {
applicationContex.publishEvent(event);
}
}
为了方便测试,新建AutoPublisher.java类,
自动产生用户新增或者删除的事件,
每隔一秒,交替发布用户新增和删除事件:
package org.springframework.context.event.publisher;
import java.util.concurrent.TimeUnit;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.event.entity.UserInfo;
import org.springframework.context.event.type.UserAddEvent;
import org.springframework.context.event.type.UserDelEvent;
import org.springframework.stereotype.Component;
@Component
public class AutoPublisher implements InitializingBean {
@Autowired
private UserEventPublisherContext userEventPublisherContext;
@Override
public void afterPropertiesSet() throws Exception {
Thread t = new Thread(new Runnable() {
boolean isAdd = true;
@Override
public void run() {
while (true) {
UserInfo user = new UserInfo();
System.out.println("Publish isAdd=" + isAdd + ", user=" + user);
if (isAdd) {
UserAddEvent event = new UserAddEvent(this, user);
userEventPublisherContext.publishUserAddEvent(event);
} else {
UserDelEvent event = new UserDelEvent(this, user);
userEventPublisherContext.publishUserDelEvent(event);
}
isAdd = !isAdd;
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
t.start();
}
}
4.监听者
监听者,即事件监听器,接收事件并对其进行处理,
监听者既可以通过实现ApplicationListener接口,
也可以通过@EventListener注解来接收事件。
新建UserEventListenerInterface.java类,
实现ApplicationListener接口,
这里只监听用户新增事件,
然后打印出对应的日志:
package org.springframework.context.event.listener;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.type.UserAddEvent;
import org.springframework.stereotype.Component;
@Component
public class UserEventListenerInterface implements ApplicationListener<UserAddEvent> {
@Override
public void onApplicationEvent(UserAddEvent event) {
System.out.println("UserEventListenerInterface.onApplicationEvent()收到消息=" + event);
}
}
新建UserEventListenerAnnotation.java类,
使用@EventListener注解,
同时监听用户新增和删除事件:
package org.springframework.context.event.listener;
import org.springframework.context.event.EventListener;
import org.springframework.context.event.type.UserAddEvent;
import org.springframework.context.event.type.UserDelEvent;
import org.springframework.stereotype.Component;
@Component
public class UserEventListenerAnnotation {
@EventListener
public void listenUserAddEvent(UserAddEvent event) {
System.out.println("UserEventListenerAnnotation.listenUserAddEvent()收到消息=" + event);
}
@EventListener
public void listenUserDelEvent(UserDelEvent event) {
System.out.println("UserEventListenerAnnotation.listenUserDelEvent()收到消息=" + event);
}
}
5.其他代码
为了进行测试,
这里使用Spring Boot启动环境,
需要一些其他辅助的代码和配置如下:
新建ContextApplication.java启动类:
package org.springframework.context.event;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class ContextApplication {
public static void main(String[] args) {
SpringApplication.run(ContextApplication.class, args);
}
}
新建application.yml配置文件:
server:
port: 8088
新建pom.xml依赖配置:
<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">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.yuwen.spring</groupId>
<artifactId>spring-basic-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<artifactId>spring-context</artifactId>
<description>spring-context相关功能测试Demo</description>
<properties>
<spring-boot.version>2.5.6</spring-boot.version>
</properties>
<dependencyManagement>
<dependencies>
<!--Spring Boot 依赖管理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<!-- Spring Boot Web服务 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- Log4j2 start 日志框架 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-log4j2</artifactId>
</dependency>
<!-- Log4j2 end -->
</dependencies>
</project>
6.运行测试
运行ContextApplication.java启动类,
输出日志如下:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.5.6)
2022-05-13 13:51:13.142 INFO 2188 --- [ main] o.s.c.e.ContextApplication : Starting ContextApplication using Java 1.8.0_191 on yuwen-asiainfo with PID 2188 (D:\Code\Learn\SpringBasic\spring-basic-demo\spring-context\target\classes started by yuwen in D:\Code\Learn\SpringBasic\spring-basic-demo\spring-context)
2022-05-13 13:51:13.150 INFO 2188 --- [ main] o.s.c.e.ContextApplication : No active profile set, falling back to default profiles: default
2022-05-13 13:51:14.632 INFO 2188 --- [ main] o.s.b.w.e.t.TomcatWebServer : Tomcat initialized with port(s): 8088 (http)
2022-05-13 13:51:14.646 INFO 2188 --- [ main] o.a.c.c.StandardService : Starting service [Tomcat]
2022-05-13 13:51:14.647 INFO 2188 --- [ main] o.a.c.c.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.54]
2022-05-13 13:51:14.716 INFO 2188 --- [ main] o.a.c.c.C.[.[.[/] : Initializing Spring embedded WebApplicationContext
2022-05-13 13:51:14.716 INFO 2188 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1510 ms
Publish isAdd=true, user=UserInfo [name=null, age=0]
UserEventListenerInterface.onApplicationEvent()收到消息=UserAddEvent [user=UserInfo [name=null, age=0]]
2022-05-13 13:51:15.038 INFO 2188 --- [ main] o.s.b.w.e.t.TomcatWebServer : Tomcat started on port(s): 8088 (http) with context path ''
2022-05-13 13:51:15.047 INFO 2188 --- [ main] o.s.c.e.ContextApplication : Started ContextApplication in 2.263 seconds (JVM running for 2.882)
Publish isAdd=false, user=UserInfo [name=null, age=0]
UserEventListenerAnnotation.listenUserDelEvent()收到消息=UserDelEvent [user=UserInfo [name=null, age=0]]
Publish isAdd=true, user=UserInfo [name=null, age=0]
UserEventListenerInterface.onApplicationEvent()收到消息=UserAddEvent [user=UserInfo [name=null, age=0]]
UserEventListenerAnnotation.listenUserAddEvent()收到消息=UserAddEvent [user=UserInfo [name=null, age=0]]
查看日志发现:
UserDeleteEvent能够被UserEventListenerAnnotation接收到;
UserAddEvent事件能够被UserEventListenerInterface和UserEventListenerAnnotation接收到。
7.发送者写法2
发送者不仅可以注入ApplicationContext发布事件,
也可以实现ApplicationEventPublisherAware接口发布事件,
新建UserEventPublisherInterface.java类如下:
package org.springframework.context.event.publisher;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.event.type.UserAddEvent;
public class UserEventPublisherInterface implements ApplicationEventPublisherAware {
private ApplicationEventPublisher applicationEventPublisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
public void publishUserAddEvent(UserAddEvent event) {
applicationEventPublisher.publishEvent(event);
}
}
顺便一提,ApplicationContext实现了ApplicationEventPublisher接口,
也可以在代码中注入ApplicationEventPublisher,
和注入ApplicationContext是等价的,
因为际还是使用ApplicationContext发布用户事件,
推荐使用ApplicationEventPublisher使代码更明确,
新建UserEventPublisher.java类如下:
package org.springframework.context.event.publisher;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.event.type.UserAddEvent;
import org.springframework.stereotype.Component;
@Component
public class UserEventPublisher {
@Autowired
private ApplicationEventPublisher applicationEventPublisher;
public void publishUserAddEvent(UserAddEvent event) {
applicationEventPublisher.publishEvent(event);
}
}
网友评论