009-Spring Boot 事件监听、监听器配置与方式、spring、Spring boot内置事件
一、概念
1.事件监听的流程
步骤一、自定义事件,一般是继承ApplicationEvent抽象类
步骤二、定义事件监听器,一般是实现ApplicationListener接口
步骤三、启动时,需要将监听器加入到Spring容器中
步骤四、发布事件
对于配置监听器的方式【即第三步】
方式一、app.addListeners(new MyApplicationListener());添加监听器
方式二、把监听器使用纳入Spring配置中管理如使用@Component标注
方式三、再application.properties中添加context.listener.classes配置项配置
方式四、使用注解@EventListener在方法上,且该类需要在Spring上管理
2、示例【方式一】:
步骤一、自定义事件MyApplicationEvent
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.lhx.spring.springboot_event; import org.springframework.context.ApplicationEvent; /** * 定义事件 * @author Administrator * */ public class MyApplicationEvent extends ApplicationEvent { private static final long serialVersionUID = 1L; public MyApplicationEvent(Object source) { super(source); } }
步骤二、定义事件监听器MyApplicationListener
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.lhx.spring.springboot_event; import org.springframework.context.ApplicationListener; public class MyApplicationListener implements ApplicationListener<MyApplicationEvent> { @Override public void onApplicationEvent(MyApplicationEvent event) { System.out.println("接收到事件:" + event.getClass()); } }
步骤三、在App启动程序中添加以及发布
@SpringBootApplication public class App { public static void main(String[] args) { // ConfigurableApplicationContext context = SpringApplication.run(App.class, // args); SpringApplication app = new SpringApplication(App.class); app.addListeners(new MyApplicationListener()); ConfigurableApplicationContext context = app.run(args); context.publishEvent(new MyApplicationEvent(new Object())); context.close(); } }
3、示例【方式二】:
注意:其实在示例步骤三,也可以用注解方式将时间监听器纳入Spring管理中
步骤一、与示例一一致
步骤二、将事件监听器添加@Component注解。
步骤三、启动类
@SpringBootApplication public class App { public static void main(String[] args) { SpringApplication app = new SpringApplication(App.class); //app.addListeners(new MyApplicationListener()); ConfigurableApplicationContext context = app.run(args); context.publishEvent(new MyApplicationEvent(new Object())); context.close(); } }
4、示例【方式四】:
步骤一、与示例一一致
步骤二、与示例一一致
步骤三、增加单独处理类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.lhx.spring.springboot_event; import org.springframework.context.event.ContextStoppedEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; @Component public class MyEventHandle { @EventListener public void event(MyApplicationEvent event) { System.out.println("MyEventHandle 接收到事件:" + event.getClass()); } @EventListener public void event2(ContextStoppedEvent event) { System.out.println("应用停止 接收到事件:" + event.getClass()); } }
步骤三、启动类
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@SpringBootApplication public class App { public static void main(String[] args) { SpringApplication app = new SpringApplication(App.class);、 ConfigurableApplicationContext context = app.run(args); context.publishEvent(new MyApplicationEvent(new Object())); context.close(); } }
二、配置监听器的方式原理
1.方式三实现,在application.properties中添加context.listener.classes配置项配置
查看:DelegatingApplicationListener
public class DelegatingApplicationListener implements ApplicationListener<ApplicationEvent>, Ordered { // NOTE: Similar to org.springframework.web.context.ContextLoader private static final String PROPERTY_NAME = "context.listener.classes";
核心逻辑
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
@Override public void onApplicationEvent(ApplicationEvent event) { if (event instanceof ApplicationEnvironmentPreparedEvent) { List<ApplicationListener<ApplicationEvent>> delegates = getListeners( ((ApplicationEnvironmentPreparedEvent) event).getEnvironment()); if (delegates.isEmpty()) { return; } this.multicaster = new SimpleApplicationEventMulticaster(); for (ApplicationListener<ApplicationEvent> listener : delegates) { this.multicaster.addApplicationListener(listener); } } if (this.multicaster != null) { this.multicaster.multicastEvent(event); } } @SuppressWarnings("unchecked") private List<ApplicationListener<ApplicationEvent>> getListeners( ConfigurableEnvironment environment) { if (environment == null) { return Collections.emptyList(); } String classNames = environment.getProperty(PROPERTY_NAME); List<ApplicationListener<ApplicationEvent>> listeners = new ArrayList<ApplicationListener<ApplicationEvent>>(); if (StringUtils.hasLength(classNames)) { for (String className : StringUtils.commaDelimitedListToSet(classNames)) { try { Class<?> clazz = ClassUtils.forName(className, ClassUtils.getDefaultClassLoader()); Assert.isAssignable(ApplicationListener.class, clazz, "class [" + className + "] must implement ApplicationListener"); listeners.add((ApplicationListener<ApplicationEvent>) BeanUtils .instantiateClass(clazz)); } catch (Exception ex) { throw new ApplicationContextException( "Failed to load context listener class [" + className + "]", ex); } } } AnnotationAwareOrderComparator.sort(listeners); return listeners; }
2、方式四实现
查看:EventListenerMethodProcessor的processBean
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
protected void processBean(final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) { if (!this.nonAnnotatedClasses.contains(targetType)) { Map<Method, EventListener> annotatedMethods = null; try { annotatedMethods = MethodIntrospector.selectMethods(targetType, new MethodIntrospector.MetadataLookup<EventListener>() { @Override public EventListener inspect(Method method) { return AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class); } }); } catch (Throwable ex) { // An unresolvable type in a method signature, probably from a lazy bean - let's ignore it. if (logger.isDebugEnabled()) { logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex); } } if (CollectionUtils.isEmpty(annotatedMethods)) { this.nonAnnotatedClasses.add(targetType); if (logger.isTraceEnabled()) { logger.trace("No @EventListener annotations found on bean class: " + targetType.getName()); } } else { // Non-empty set of methods for (Method method : annotatedMethods.keySet()) { for (EventListenerFactory factory : factories) { if (factory.supportsMethod(method)) { Method methodToUse = AopUtils.selectInvocableMethod( method, this.applicationContext.getType(beanName)); ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse); if (applicationListener instanceof ApplicationListenerMethodAdapter) { ((ApplicationListenerMethodAdapter) applicationListener) .init(this.applicationContext, this.evaluator); } this.applicationContext.addApplicationListener(applicationListener); break; } } } if (logger.isDebugEnabled()) { logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" + beanName + "': " + annotatedMethods); } } } }
EventListener
annotatedMethods = MethodIntrospector.selectMethods(targetType, new MethodIntrospector.MetadataLookup<EventListener>() { @Override public EventListener inspect(Method method) { return AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class); } });
查看for
for (Method method : annotatedMethods.keySet()) { for (EventListenerFactory factory : factories) { if (factory.supportsMethod(method)) { Method methodToUse = AopUtils.selectInvocableMethod( method, this.applicationContext.getType(beanName)); ApplicationListener<?> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse); if (applicationListener instanceof ApplicationListenerMethodAdapter) { ((ApplicationListenerMethodAdapter) applicationListener) .init(this.applicationContext, this.evaluator); } this.applicationContext.addApplicationListener(applicationListener); break; } } }
其中factory即EventListenerFactory factory
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
/* * Copyright 2002-2015 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.springframework.context.event; import java.lang.reflect.Method; import org.springframework.context.ApplicationListener; /** * Strategy interface for creating {@link ApplicationListener} for methods * annotated with {@link EventListener}. * * @author Stephane Nicoll * @since 4.2 */ public interface EventListenerFactory { /** * Specify if this factory supports the specified {@link Method}. * @param method an {@link EventListener} annotated method * @return {@code true} if this factory supports the specified method */ boolean supportsMethod(Method method); /** * Create an {@link ApplicationListener} for the specified method. * @param beanName the name of the bean * @param type the target type of the instance * @param method the {@link EventListener} annotated method * @return an application listener, suitable to invoke the specified method */ ApplicationListener<?> createApplicationListener(String beanName, Class<?> type, Method method); }
三、spring、Spring boot内置事件
1.Spring
jar包:Spring-context-4.3.13.RELEASE
包:org.springframwork.context.event;
常用:ContextClosedEvent、ContextStartedEvent、ContextStopedEvent
示例
@Component public class MyEventHandle { @EventListener public void event2(ContextStoppedEvent event) { System.out.println("应用停止 接收到事件:" + event.getClass()); } }
当然:app中Context要停止
@SpringBootApplication public class App { public static void main(String[] args) { SpringApplication app = new SpringApplication(App.class); ConfigurableApplicationContext context = app.run(args); context.publishEvent(new MyApplicationEvent(new Object())); context.stop(); } }
2.Spring-boot
jar包:Spring-boot-1.5.9.RELEASE
包:org.springframework.boot.context.event;
常用:ApplicationEnvironmentPreparedEvent、ApplicationFailedEvent等
内置时间执行时机:
Connected to the target VM, address: '127.0.0.1:55584', transport: 'socket' 0---------ApplicationEvent Type:org.springframework.boot.context.event.ApplicationStartingEvent 1---------ApplicationEvent Type:org.springframework.boot.context.event.ApplicationEnvironmentPreparedEvent . ____ _ __ _ _ /\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \ ( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \ \\/ ___)| |_)| | | | | || (_| | ) ) ) ) ' |____| .__|_| |_|_| |_\__, | / / / / =========|_|==============|___/=/_/_/_/ :: Spring Boot :: (v2.7.8) 2023-03-27 09:48:16,136 --- INFO --- [background-preinit] --- [util.Version] --- [] --- HV000001: Hibernate Validator 6.2.5.Final 2---------ApplicationEvent Type:org.springframework.boot.context.event.ApplicationContextInitializedEvent 2023-03-27 09:48:16,254 --- INFO --- [main] --- [openapi.OpenapiApplication] --- [] --- Starting OpenapiApplication using Java 1.8.0_151 on B-92SYMD6R-2222.local with PID 32623 (/Users/lihongxu/work/IdeaProjects/gateway-demo/app/target/classes started by lihongxu in /Users/lihongxu/work/IdeaProjects/gateway-demo) 2023-03-27 09:48:16,257 --- INFO --- [main] --- [openapi.OpenapiApplication] --- [] --- The following 1 profile is active: "dev" 3---------ApplicationEvent Type:org.springframework.boot.context.event.ApplicationPreparedEvent 1 2023-03-27 09:48:17,049 --- INFO --- [main] --- [tomcat.TomcatWebServer] --- [] --- Tomcat initialized with port(s): 7003 (http) 7004 (http) 2023-03-27 09:48:17,064 --- INFO --- [main] --- [http11.Http11NioProtocol] --- [] --- Initializing ProtocolHandler ["http-nio-7003"] 2023-03-27 09:48:17,065 --- INFO --- [main] --- [http11.Http11NioProtocol] --- [] --- Initializing ProtocolHandler ["http-nio-7004"] 2023-03-27 09:48:17,078 --- INFO --- [main] --- [core.StandardService] --- [] --- Starting service [Tomcat] 2023-03-27 09:48:17,078 --- INFO --- [main] --- [core.StandardEngine] --- [] --- Starting Servlet engine: [Apache Tomcat/9.0.71] 2023-03-27 09:48:17,293 --- INFO --- [main] --- [[localhost].[/]] --- [] --- Initializing Spring embedded WebApplicationContext 2023-03-27 09:48:17,293 --- INFO --- [main] --- [context.ServletWebServerApplicationContext] --- [] --- Root WebApplicationContext: initialization completed in 1000 ms 4---------ApplicationEvent Type:PostConstruct 5---------ApplicationEvent Type:InitializingBean.afterPropertiesSet 2023-03-27 09:48:17,606 --- INFO --- [main] --- [http11.Http11NioProtocol] --- [] --- Starting ProtocolHandler ["http-nio-7003"] 2023-03-27 09:48:17,612 --- INFO --- [main] --- [http11.Http11NioProtocol] --- [] --- Starting ProtocolHandler ["http-nio-7004"] 2023-03-27 09:48:17,615 --- INFO --- [main] --- [tomcat.TomcatWebServer] --- [] --- Tomcat started on port(s): 7003 (http) 7004 (http) with context path '' 6---------ApplicationEvent Type:org.springframework.boot.web.servlet.context.ServletWebServerInitializedEvent 7---------ApplicationEvent Type:org.springframework.context.event.ContextRefreshedEvent 2023-03-27 09:48:17,623 --- INFO --- [main] --- [openapi.OpenapiApplication] --- [] --- Started OpenapiApplication in 1.881 seconds (JVM running for 3.536) 8---------ApplicationEvent Type:org.springframework.boot.context.event.ApplicationStartedEvent 9---------ApplicationEvent Type:org.springframework.boot.availability.AvailabilityChangeEvent 10---------ApplicationEvent Type:org.springframework.boot.context.event.ApplicationReadyEvent 11---------ApplicationEvent Type:org.springframework.boot.availability.AvailabilityChangeEvent
相关文章
- Spring 全家桶之 Spring Boot 2.6.4(六)- Web Develop(Part C)
- Apache Shiro In Easy Steps With Spring Boot(二)-Authenticator,Authorizer,Subject
- Spring Boot 日志管理
- Spring Boot 2.x: 定时给对象发送天气
- 网关 Spring Cloud Gateway - API 调用的组织者
- 【07】Spring源码-分析篇-refresh方法
- spring boot整合shiro_Spring框架介绍及使用
- 高频面试题:谈谈你对 Spring Boot 自动装配机制的理解
- spring boot项目读取配置文件的参数
- Spring Ioc底层实现
- Spring Boot配置线程池使用多线程插入数据
- 面试遇到Spring双层事务不回滚怎么破
- Java项目分享-基于Spring Boot的迷你天猫商城
- 【Spring boot Nacos系列教程】Nacos第三篇:多环境配置
- Spring核心之控制反转IOC
- 云原生时代的Spring Boot 3.0: GraalVM原生镜像,启动速度提升近30倍
- Spring Boot 3 将于 2022 年 11 月发布,延迟了对 Java 模块系统的支持
- Spring Boot的应用启动和关闭
- Spring Boot的数据绑定和参数传递(二)
- Spring Boot的异常处理和错误页面
- Spring Boot的性能优化(一)
- 基于 Spring Aop 环绕通知实现 Redis 缓存双删功能(示例代码)
- Spring Boot 单元测试详解+实战教程编程语言
- Spring Boot 2 (八):Spring Boot 集成 Memcached详解编程语言
- Spring Boot(五):spring data jpa的使用详解编程语言
- Spring setUsername方法:设置访问数据库的用户名
- Linux系统如何进入Boot界面(linux进入boot)
- Spring Boot 2.x :通过 spring-boot-starter-hbase 集成 HBase