EventListener

一、@EventListener

演示案例
写一个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> T getBean(String name) {
		return (T) applicationContext.getBean(name);
	}

	/**
	 * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
	 */
	public static <T> T getBean(Class<T> 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> T getBean(String name) {
		return (T) applicationContext.getBean(name);
	}

	/**
	 * 从静态变量applicationContext中取得Bean, 自动转型为所赋值对象的类型.
	 */
	public static <T> T getBean(Class<T> 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中的监听方法。
 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值