Spring中的事件监听
ApplicationContext 中的事件处理是通过 ApplicationEvent 类和 ApplicationListener 接口实现的。如果一个 Bean(监听器实例)实现了 ApplicationListener 接口,并被部署到 Spring 容器中,那么每次在容器中发布一个 ApplicationEvent 事件,该 Bean (监听器实例)都会被通知到。本质上,这种机制就是一个观察者的设计模式。// 事件、监听器、观察者模式。
我们如何自定义一个事件呢?
基于SpringBoot简单实现一个案例:
springboot的版本如下:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.2.RELEASE</version>
<relativePath/>
</parent>
引入的依赖如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
1、首先定义要给登录成功的事件
// 定义一个登录成功的事件,继承ApplicationContext类,因为该类中只有一个有参构造,所以这里我们要调用父类的有参构造并传入参数
public class LoginOKEvent extends ApplicationEvent {
public LoginOKEvent(Object source) {
super(source);
}
}
2、定义要给事件监听器
// 监听LoginOKEvent这个事件
@Service
public class LoginListener implements ApplicationListener<LoginOKEvent> {
// 自定义发送邮件的逻辑
public void sendEmail(String address, String content) {
System.out.println("发送邮件的逻辑。。。");
}
// 如果监听到了LoginOKEvent这个事件,就会自己调用这个方法
@Override
public void onApplicationEvent(LoginOKEvent event) {
this.sendEmail("aaa@qq.com","haha");
}
}
3、在登录校验成功之后发布登录成功的事件
@Service
public class LoginService {
@Autowired
private ApplicationContext applicationContext;
public void login(String username, String password) {
// 登录校验的逻辑
System.out.println("用户名密码正确");
// 登录成功发布登录成功的事件
applicationContext.publishEvent(new LoginOKEvent(this));
}
}
4、测试方法
@SpringBootTest
public class Test {
@Autowired
private LoginService loginService;
@org.junit.jupiter.api.Test
public void testSpringEvent() {
loginService.login("张三","123");
}
}
执行结果
此时就实现了我们上图的效果
如果我们就算更换逻辑,只要添加新的监听者就行,不用修改原来的代码了。
比如,实现在登录成功之后给用户发送短信:
// 监听LoginOKEvent这个事件
@Service
public class LoginListener implements ApplicationListener<LoginOKEvent> {
// 自定义发送邮件的逻辑
public void sendMessage() {
System.out.println("发送短信的逻辑。。。");
}
// 如果监听到了LoginOKEvent这个事件,就会自己调用这个方法
@Override
public void onApplicationEvent(LoginOKEvent event) {
this.sendEmail();
}
}
这种写法叫做:“以增量的方式应对变化的需求”,而不是去修改已有的代码。假设有B接口调用了C接口,你修改了C接口,那么B接口可能业务结果就错了,此时调用B接口的A接口也可能受到影响,是连锁反应。所以,一般我们都提倡“对扩展开放,对修改关闭”的原则。
在上面的监听者的代码中还是有一些麻烦,因为我们仅仅是为了监听事件然后回调方法,但是还需要实现一个类就显得有些麻烦了,这里Spring提供了注解的写法
// 监听LoginOKEvent这个事件
@Service
public class LoginListener {
// 自定义发送邮件的逻辑
@EventListener(LoginOKEvent)
public void sendMessage() {
System.out.println("发送短信的逻辑。。。");
}
}
然后Spring当中的事件监听基本就基于Springboot说完了,这里还存在一个小的问题,就是这里我们是采用同步的方式进行执行的发布的事件然后监听者执行方法,我们为了提高性能,不阻塞主线程,可以考虑异步的实现方式,例如:
@Service
public class LoginService {
@Autowired
private ApplicationContext applicationContext;
public void login(String username, String password) {
// 登录校验的逻辑
System.out.println("用户名密码正确");
// 创建线程使用异步的方式来发布事件
new Thread(() ->{
// 登录成功发布登录成功的事件
applicationContext.publishEvent(new LoginOKEvent(this));
}).start();
}
}
SpringSPI也可以实现类似的效果,可以根据实际业务需求考虑。