演示案例
写一个Userservice,在方法上标注@EventListener,属性是监听的事件Class。
如果监听到此事件Class,则会执行被注解的方法。
@Service
public class UserService {
@EventListener(classes={ApplicationEvent.class})
public void listen(ApplicationEvent event){
System.out.println("UserService。。监听到的事件:"+event);
}
@EventListener(classes = {Tom.class, Jerry.class})
public void tom(Object event){
System.out.println("tom---------------"+event);
if (event instanceof Tom){
System.out.println("tom");
}
if (event instanceof Jerry)
System.out.println("jerry");
}
}
事件类Tom继承ApplicationEvent:
public class Tom extends ApplicationEvent {
/**
* Create a new ApplicationEvent.
*
* @param source the object on which the event initially occurred (never {@code null})
*/
public Tom(Object source) {
super(source);
}
}
Jerry类:
/**
*事件类Jerry:
*/
public class Jerry {
}
单元测试:
@RestController("/test")
public class Test {
public static void main(String[] args) {
jpasypt();
modifier();
}
@GetMapping("/event")
public void test() {
//发布事件;
SpringContextHolder.publishEvent(new ApplicationEvent(new String("我发布的时间")) {
});
SpringContextHolder.publishEvent(new Jerry());
SpringContextHolder.publishEvent(new Tom(new String("hi tom")));
}
}
SpringContextHolder工具类:
/**
* @author yrh
* @date 2022/2/1 Spring 工具类
*/
@Slf4j
@Service
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 实现ApplicationContextAware接口, 注入Context到静态变量中.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextHolder.applicationContext = applicationContext;
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static T getBean(String name) {
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static T getBean(Class requiredType) {
return applicationContext.getBean(requiredType);
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
public static void clearHolder() {
if (log.isDebugEnabled()) {
log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
}
applicationContext = null;
}
/**
* 发布事件
* @param event
*/
public static void publishEvent(ApplicationEvent event) {
if (applicationContext == null) {
return;
}
applicationContext.publishEvent(event);
}
/**
* 实现DisposableBean接口, 在Context关闭时清理静态变量.
*/
@Override
@SneakyThrows
public void destroy() {
SpringContextHolder.clearHolder();
}
}
发布的ApplicationEvent事件,只有监听ApplicationEvent.class的方法能收到。
发布的Tom事件,监听Tom 和 ApplicationEvent 的方法都能收到。
发布的Jerry事件,只有监听Jerry的方法能收到。
应用事件:
/**
* @author xfn
* @Classname com.xx.xxx.admin.test.TestEvent
* @Description TestEvent
* @Date 2022/5/9 9:35
*/
public class TestEvent extends ApplicationEvent {
public TestEvent(SysLog source) {
super(source);
}
}
SysLog实体:
@Data
@EqualsAndHashCode(callSuper = true)
public class SysLog extends BaseEntity {
private static final long serialVersionUID = 1L;
/**
* 编号
*/
@TableId(value = "id", type = IdType.ASSIGN_ID)
@ApiModelProperty(value = "日志编号")
@JsonSerialize(using = ToStringSerializer.class)
private Long id;
/**
* 日志类型
*/
@NotBlank(message = "日志类型不能为空")
@ApiModelProperty(value = "日志类型")
@FieldBind(type = "log_type", target = "typeText")
private String type;
@TableField(exist = false)
private String typeText;
/**
* 日志标题
*/
@NotBlank(message = "日志标题不能为空")
@ApiModelProperty(value = "日志标题")
private String title;
/**
* 操作IP地址
*/
@ApiModelProperty(value = "操作ip地址")
private String remoteAddr;
/**
* 用户浏览器
*/
@ApiModelProperty(value = "用户代理")
private String userAgent;
/**
* 请求URI
*/
@ApiModelProperty(value = "请求uri")
private String requestUri;
/**
* 操作方式
*/
@ApiModelProperty(value = "请求方式")
private String method;
/**
* 操作提交的数据
*/
//@ExcelProperty("请求参数")
@ApiModelProperty(value = "数据")
private String params;
/**
* 执行时间
*/
//@ExcelProperty("方法执行时间")
@ApiModelProperty(value = "方法执行时间")
private Long time;
/**
* 异常信息
*/
//@ExcelProperty("异常信息")
@ApiModelProperty(value = "异常信息")
private String exception;
/**
* 服务ID
*/
//@ExcelProperty("应用标识")
@ApiModelProperty(value = "应用标识")
private String serviceId;
}
监听器:
/**
* @author xfn
* @Classname com.xx.xxx.admin.test.TestListener
* @Description TestListener
* @Date 2022/5/9 9:38
*/
@Slf4j
@RequiredArgsConstructor
@Component
@EnableAsync
public class TestListener {
@Order
@Async
@EventListener(TestEvent.class)
public void doListener(TestEvent event) {
SysLog source = (SysLog)event.getSource();
System.out.println("dddddddddddddddddddddd");
log.info(source.toString());
System.out.println(source);
}
}
调用:
@RestController("/test")
public class Test {
public static void main(String[] args) {
jpasypt();
modifier();
}
@GetMapping("/event")
public void test() {
SysLog sysLog = new SysLog();
sysLog.setId(1111L);
sysLog.setException("sssffff");
sysLog.setCreator("ss");
sysLog.setTime(3L);
sysLog.setCreateTime(LocalDateTime.now());
SpringContextHolder.publishEvent(new TestEvent(sysLog));
}
}
SpringContextHolder工具类:
/**
* @author yrh
* @date 2022/2/1 Spring 工具类
*/
@Slf4j
@Service
@Lazy(false)
public class SpringContextHolder implements ApplicationContextAware, DisposableBean {
private static ApplicationContext applicationContext = null;
/**
* 取得存储在静态变量中的ApplicationContext.
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 实现ApplicationContextAware接口, 注入Context到静态变量中.
*/
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextHolder.applicationContext = applicationContext;
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
@SuppressWarnings("unchecked")
public static T getBean(String name) {
return (T) applicationContext.getBean(name);
}
/**
* 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
*/
public static T getBean(Class requiredType) {
return applicationContext.getBean(requiredType);
}
/**
* 清除SpringContextHolder中的ApplicationContext为Null.
*/
public static void clearHolder() {
if (log.isDebugEnabled()) {
log.debug("清除SpringContextHolder中的ApplicationContext:" + applicationContext);
}
applicationContext = null;
}
/**
* 发布事件
* @param event
*/
public static void publishEvent(ApplicationEvent event) {
if (applicationContext == null) {
return;
}
applicationContext.publishEvent(event);
}
/**
* 实现DisposableBean接口, 在Context关闭时清理静态变量.
*/
@Override
@SneakyThrows
public void destroy() {
SpringContextHolder.clearHolder();
}
}
EventListenr注释了EventListenerMethodProcessor
EventListenerMethodProcessor实现了SmartInitializingSingleton 接口
SmartInitializingSingleton接口
关键在SmartInitializingSingleton接口,看注释,当所有单实例创建完成后,调用afterSingletonsInstantiated方法。
如何保证容器中所有的单实例创建完成后,会执行实现SmartInitializingSingleton接口实例的afterSingletonsInstantiated方法?
答案在容器refresh方法的最后一步,finishBeanFactoryInitialization方法中的beanFactory.preInstantiateSingletons() (创建剩余的单实例);
org.springframework.context.support.AbstractApplicationContext#refresh
先遍历beanNames,去创建实例,创建完成后又遍历beanNames,判断实例是否实现SmartInitializingSingleton接口,实现则执行实例的afterSingletonsInstantiated方法
回到EventListenerMethodProcessor
在EventListenerMethodProcessor的afterSingletonsInstantiated方法打断点
内容是遍历容器所有beanNames,因为我们在Test类上加了@EventListener注解,所以把beanName遍历到Test看如何执行的。
通过beanName尝试找到 bean对应的Class, AutoProxyUtils.determineTargetClass(this.applicationContext.getBeanFactory(), beanName);
执行到processBean
this.nonAnnotatedClasses.contains(targetType) :判断targetType 代表的Class 是否标注了注解。
annotatedMethods = MethodIntrospector.selectMethods : 得到targetType 中被注解的方法, Test类有test方法被注解.
如果当前beanName代表的类targetype上没有注解,就加入到nonAnnotatedClasses。
每个带@EventListener方法就会创建一个ApplicationListener对象
接着上面,得到所有遍历注解的方法后, 遍历
factory.supportsMethod(method) :看 EventListenerFactory 是否支持这个方法
ApplicationListener> applicationListener = factory.createApplicationListener(beanName, targetType, methodToUse)
如果支持就用 EventListenerFactory 创建一个 ApplicationListener ,如果创建出来是 ApplicationListenerMethodAdapter适配器,就再初始化一下,总之得到一个ApplicationListener
ApplicationListenerMethodAdapter是 ApplicationListener的子类。
this.applicationContext.addApplicationListener(applicationListener): 把创建的ApplicationListener加入到了 容器中!
如何加入容器? 其实是加到了多播器中 applicationEventMulticaster
总结下:方法如果注解了@EventListener,就会创建一个ApplicationListener 或者是ApplicationListenerAdpter加入到applicationEventMulticaster中。Test有一个方法标了注解,就创建了一个监听器。
发布事件如何找到对应的监听方法
虽然创建了监听器,之前也讲过可以通过 事件寻找到订阅它的监听器,但在发布事件时是如何调用到Test的方法的,毕竟执行方法还是在Test,并不是在ApplicationListener,而且看断点信息创建的ApplicationListener并不是一个代理对象拥有对应的Test的方法。
把断点打到Test的监听方法上
看执行流程,最前面几步还是 发布事件---从多播器找监听器---执行监听方法 onApplicationEvent
从onApplicationEvent方法开始不同,如果我们自定义实现 ApplicationListener 接口,则从这一步会直接执行我们自定义的方法。
但现在用的@EventListener注解,看断点目前执行到的是 ApplicationListenerMethodAdapter,一个监听适配器,在为监听注解方法创建 监听器时 创建的就是这个 监听适配器。
执行监听方法,ApplicationListenerMethodAdapter.onApplicationEvent 调用的是 processEvent(event);
重点来了,在 processEvent让断点进来。
Object[] args = resolveArguments(event):解析事件对象,得到事件的负载核和一些信息
shouldHandle判断是否应该处理。
doInvoke 通过事件信息得到了 事件对应的标注@EventListener 的TestListener实例.
原来是通过beanName寻找在容器中的 实例,因为创建ApplicationListener时保存了 对应方法类的信息。通过Test的beanName找到了Test#TestListener实例。
this.bridgedMethod.invoke(bean, args);通过反射调用了 Test中的监听方法。
三.总结
自定义实现ApplicationListener接口的流程:发布事件时,通过此事件找到 多播器、找订阅它的监听器,然后执行监听方法。
如果用@EventListener注解,则发布事件时找到是监听适配器(ApplicationListenerAdpter),监听适配器是监听器的子类,在创建ApplicationListenerAdpter时,其中保存了注解@EventListene的bean的信息,然后通过监听适配器从容器中找到对应bean,再执行bean中的监听方法。