事件监听机制(五)再话Jdk事件监听到Spring框架事件监听
事件监听机制(五)再话Jdk事件监听到Spring框架事件监听
背景
熟悉Spring
框架的同学都清楚,Spring
容器启动到销毁事件监听机制始终贯穿,那么Jdk
中是否也存在相应的监听机制呢,我们一起进行深入的学习和研究。
概念
事件源
事件对象的产生者,任何一个事件都有一个来源。
事件监听器注册表
当事件框架或组件收到一个事件后,需要通知所有相关的事件监听器来进行处理,这个时候就需要有个存储监听器的地方,也就是事件监听器注册表。
事件广播器
事件广播器在整个事件机制中扮演一个中介的角色,当事件发布者发布一个事件后,就需要通过广播器来通知所有相关的监听器对该事件进行处理。
Jdk实现
用户实体
public class User {
private Long id;
private String name;
public Long getId() { return id;}
public void setId(Long id) { this.id = id;}
public String getName() { return name;}
public void setId(String name) { this.name= name;}
public User(Long id, String name) { this.id = id;this.name= name;}
}
用户事件
public class UserEvent extends EventObject {
public UserEvent(Object source) { super(source);}
}
消息事件
public class MsgEvent extends UserEvent{
public MsgEvent(Object source) { super(source);}
}
用户监听器
public interface UserListener extends EventListener {
// 处理用户注册事件
void onRegister(UserEvent event);
}
消息监听器
public class MsgListener implements UserListener {
@Override
public void onRegister(UserEvent event) {
if (event instanceof MsgEvent){
User user = (User) event.getSource();
System.out.println("向User id: " + user.getId() + "User name: " + user.getName() + "的用户发送消息");
}
}
}
用户服务
public class UserService {
// 监听器列表
private final List<UserListener> listeners = new ArrayList<>();
// 添加监听器
public void addListener(UserListener userListener){
this.listeners.add(userListener);
}
// 注册用户并发布用户注册事件
public void register(User user){
System.out.println("注册用户,id为:" + user.getId());
publishEvent(new MsgEvent(user));
}
// 广播用户注册事件
private void publishEvent(UserEvent event){
for (UserListener listener : listeners) {
listener.onRegister(event);
}
}
public static void main(String[] args) {
UserService userService = new UserService();
User user = new User(1L,sxs);
userService.addListener(new MsgListener());
userService.register(user);
}
}
// 注册用户,id为:1
// 向User id: 1 User name: sxs的用户发送消息
Spring实现
ApplicationEvent
是Spring
事件的顶层抽象类,代表事件本身,继承自JDK
的EventObject
。Spring
提供了一些内置事件,如ContextClosedEvent
、ContextStartedEvent
、ContextRefreshedEvent
、ContextStopedEvent
等。
ApplicationListener
是Spring
事件监听器顶层接口,所有的监听器都实现该接口,继承自JDK
的 EventListener
Spring
提供了一些易扩展的接口,如SmartApplicationListener
、GenericApplicationListener
ApplicationEventPublisher
是Spring
的事件发布接口,事件源通过该接口的publishEvent
方法发布事件,所有的应用上下文都具备事件发布能力,因为ApplicationContext
继承了该接口。
ApplicationEventMulticaster
是Spring
事件机制中的事件广播器,它默认提供一个SimpleApplicationEventMulticaster
实现,如果用户没有自定义广播器,则使用默认的(初始化逻辑见AbstractApplicationContext#refresh
方法)。它通过父类AbstractApplicationEventMulticaster#getApplicationListeners
方法从事件注册表中获取事件监听器,并且通过invokeListener
方法执行监听器的具体逻辑。
执行逻辑
默认的广播器是同步调用监听器的执行逻辑,但是可以通过为广播器配置Executor
实现监听器的异步执行。
事件广播器
在Spring
中通常是ApplicationContext
本身担任监听器注册表的角色,在其子类AbstractApplicationContext
中就聚合了事件广播器ApplicationEventMulticaster
和事件监听器ApplicationListnener
,并且提供注册监听器的addApplicationListnener
方法。
事件发布
当一个事件源产生事件时,它通过事件发布器ApplicationEventPublisher
发布事件,然后事件广播器ApplicationEventMulticaster
会去事件注册表ApplicationContext
中找到事件监听器ApplicationListnener
,并且逐个执行监听器的onApplicationEvent
方法,从而完成事件监听器的逻辑。
MailSendEvent
public class MailSendEvent extends ApplicationContextEvent {
private String to; //目的地
public MailSendEvent(ApplicationContext source, String to) {
super(source);
this.to = to;
}
public String getTo(){
return this.to;
}
}
MailSendListener
@Component
public class MailSendListener implements ApplicationListener<MailSendEvent>{
@Override
public void onApplicationEvent(MailSendEvent mailSendEvent) {
MailSendEvent event = mailSendEvent;
System.out.println("MailSender向"+ event.getTo()+ "发送了邮件");
}
}
MailSender
@Component("mailSender")
public class MailSender {
@Autowired
private ApplicationContext applicationContext; //容器事件由容器触发
public void sendMail(String to){
System.out.println("MailSender开始发送邮件");
MailSendEvent event = new MailSendEvent(applicationContext,to);
applicationContext.publishEvent(event);
}
}
SpringEventTest
public class SpringEventTest {
public static void main(String[] args) {
// method1
ApplicationContext context = new ClassPathXmlApplicationContext("web/WEB-INF/applicationContext.xml");
context .refresh();
MailSender sender = (MailSender)context.getBean("mailSender");
sender.sendMail("上海");
// method2
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class);
ctx.refresh();
MailSender sender = (MailSender)ctx.getBean("mailSender");
sender.sendMail("石庄");
}
}
SpringBoot实现
SpringApplicationEvent
是SpringBoot
事件的顶层抽象类,且SpringBoot
内置了7
个事件:
ApplicationStartingEvent
SpringBoot
启动开始的时候发布的事件。
ApplicationEnvironmentPreparedEvent
SpringBoot
对应Environment
准备完毕时发布的事件,此时上下文还没有创建,该事件中可以获取配置信息。通过监听该事件可以修改默认的配置信息,配置中心就是借助该事件完成将远端配置放入应用中。
ApplicationContextInitializedEvent
当准备好上下文,并且在加载任何Bean
之前发布的事件。该事件可以获取上下文信息。
ApplicationPreparedEvent
上下文创建完成时发布的事件,此时的Bean
还没有完全加载完成,只是加载了部分特定Bean
。该事件可以获取上下文信息,但是无法获取自定义Bean
,因为还没有被加载。
ApplicationStartedEvent
上下文刷新(完成所有Bean
的加载)之后,但是还没有调用任何ApplicationRunner
和CommandLineRunner
运行程序之前发布的事件。该事件同样可以获取上下文信息,并且可以获取自定义的Bean
。
ApplicationReadyEvent
完成ApplicationRunner
和CommandLineRunner
运行程序调用之后发布的事件,此时的SpringBoot
已全部启动完成。该事件同样可以获取上下文信息。
ApplicationFailedEvent
SpringBoot
启动异常时发布的事件。该事件可以获取上下文信息和异常信息,通过该事件可以友好地完成资源的回收。
程序入口
SpringApplication.run(......)
SpringBoot
的生命周期如下接口所示,会在各个生命周期广播响应的事件,调用实际的监听器。
public interface SpringApplicationRunListener {
// ApplicationStartingEvent
default void starting() {
}
// ApplicationEnvironmentPreparedEvent
default void environmentPrepared(ConfigurableEnvironment environment) {
}
// ApplicationContextInitializedEvent
default void contextPrepared(ConfigurableApplicationContext context) {
}
// ApplicationPreparedEvent
default void contextLoaded(ConfigurableApplicationContext context) {
}
// ApplicationStartedEvent
default void started(ConfigurableApplicationContext context) {
}
// ApplicationReadyEvent
default void running(ConfigurableApplicationContext context) {
}
// ApplicationFailedEvent
default void failed(ConfigurableApplicationContext context, Throwable exception) {
}
}
上述接口提供了唯一的实现类EventPublishingRunListener
,该类聚合了SpringApplication
和SimpleApplicationEventMulticaster
,前4
个事件的发布由广播器完成,后3
个委托ApplicationContext
完成发布,其实最终都是由广播器负责发布。
监听器使用方式
1、通过SPI
机制配置
在resources
目录下新建META-INF
目录,并创建名为spring.factories
的文件,在文件中声明以ApplicationListener
接口全路径为键的配置,配置内容为ApplicationListener
实现类的全路径,若有多个以逗号间隔,示例如下:
org.springframework.context.ApplicationListener=\
com.lyentech.bdc.tuya.listener.HunterApplicationContextInitializedListener,\
com.lyentech.bdc.tuya.listener.HunterApplicationEnvironmentPreparedListener,\
com.lyentech.bdc.tuya.listener.HunterApplicationFailedListener,\
com.lyentech.bdc.tuya.listener.HunterApplicationPreparedListener,\
com.lyentech.bdc.tuya.listener.HunterApplicationReadyListener,\
com.lyentech.bdc.tuya.listener.HunterApplicationStartedListener,\
com.lyentech.bdc.tuya.listener.HunterApplicationStartingListener
Spring
在启动的时候会加载spring.factories
中配置的监听器实现类,并在合适的阶段通过发布事件触发相应监听器的逻辑。此方式可以实现针对任何事件(SpringBoot
内置7
个事件都支持)监听器的成功加载。
2、注解实现
@Component
public class CustomizeListener {
@EventListener
public void listenerApplicationStartingEvent(ApplicationStartingEvent event){
System.err.println("ApplicationStartingEvent事件");
}
@Async // 异步支持,需要开启@EnableAsync
@EventListener
public void listenerApplicationStartedEvent(ApplicationStartedEvent event){
System.err.println("ApplicationStartedEvent事件");
}
@EventListener
public void listenerApplicationReadyEvent(ApplicationReadyEvent event){
System.err.println("ApplicationReadyEvent事件");
}
}
总结
以上为事件监听机制的详细实现,由此可实现代码解耦,便于模块化开发,相关的MQ
中间件设计,也是基于此思想,对于理解消息解耦、监听者模式等有很大帮助。
更多文章
事件监听机制(一)Java事件监听
https://blog.csdn.net/shang\_xs/article/details/87911756
事件监听机制(二)Spring事件监听
https://blog.csdn.net/shang\_xs/article/details/88048545
事件监听机制(三)Spring Cloud Bus流程分析
https://blog.csdn.net/shang\_xs/article/details/88050196
事件监听机制(四)从Java事件监听到Spring事件监听
https://blog.csdn.net/shang\_xs/article/details/86560994
事件监听机制(五)再话Jdk事件监听到Spring框架事件监听
https://blog.csdn.net/shang\_xs/article/details/119794917
还没有评论,来说两句吧...