简介
Spring的事件监听是通过注册在ApplicationContext的 ApplicationEvent 和 ApplicationListener 提供的。将ApplicationListener 注册在ApplicationContext后。通过ApplicationEventPublisher发布事件后,都会通知到ApplicationListener 中。实际上,这就是标准的观察者模式(Observer)。
在Spring中,默认已经实现了6种标准的事件
ContextRefreshedEvent
在ApplicationContext初始化以及刷新的时候会发布ContextRefreshedEvent事件,例如,ConfigurableApplicationContext在执行refresh()方法的时候,就会发布事件,对于ApplicationContext来说,只要上下文没有关闭,并且可以执行热刷新的操作,就可以重复触发这个事件。初始化完成,意味着加载完成所有的bean,并且执行了后处理方法,并且ApplicationContext对象可用。 例如,XmlWebApplicationContext支持热刷新,但GenericApplicationContext,AnnotationConfigApplicationContext不支持
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
.....
//在这里检查所有添加到ApplicationContext的监听器,并且注册
// Check for listener beans and register them.
registerListeners();
....
// Last step: publish corresponding event.
finishRefresh();
}
....
}
在finishRefresh()中
/**
* Finish the refresh of this context, invoking the LifecycleProcessor's
* onRefresh() method and publishing the
* {@link org.springframework.context.event.ContextRefreshedEvent}.
*/
protected void finishRefresh() {
// Clear context-level resource caches (such as ASM metadata from scanning).
clearResourceCaches();
// Initialize lifecycle processor for this context.
initLifecycleProcessor();
// Propagate refresh to lifecycle processor first.
getLifecycleProcessor().onRefresh();
// Publish the final event. //在这里发布了刷新事件
publishEvent(new ContextRefreshedEvent(this));
// Participate in LiveBeansView MBean, if active.
LiveBeansView.registerApplicationContext(this);
}
ContextStartedEvent
在ApplicationContext的start()方法执行后,会发布ContextStartedEvent事件,start()方法以为着所有的bean都会收到一个显示的信号,这个信号通常是重新启动停止的bean,或者没有自动启动的bean,
@Override
public void start() {
getLifecycleProcessor().start();
publishEvent(new ContextStartedEvent(this));
}
ContextStoppedEvent
@Override
public void stop() {
getLifecycleProcessor().stop();
publishEvent(new ContextStoppedEvent(this));
}
ContextClosedEvent
protected void doClose() {
.....
try {
// Publish shutdown event.
publishEvent(new ContextClosedEvent(this));
}
.....
}
}
RequestHandledEvent
ServletRequestHandledEvent
具体使用
使用ApplicationEvent来发布事件
首先定义1个继承与 ApplicationEvent 的对象实体
@Data
public class CreateManger extends ApplicationEvent {
private String data;
public CreateManger(ApplicationContext source,String data) {
super(source);
this.data = data;
}
}
然后在定义1个监听器,用来监听事件
public class BaseListener implements ApplicationListener<CreateManger> {
@Override
public void onApplicationEvent(CreateManger event) {
System.out.println(event.getData());
}
}
对于Spring已经实现的事件,我们只需要添加监听器,不用添加监听对象
public class StartListener implements ApplicationListener< ContextStartedEvent > {
@Override
public void onApplicationEvent(ContextStartedEvent event) {
System.out.println("onApplicationEvent:"+event.getTimestamp());
}
}
最后我们再将监听器注册进我们的上下文中
@Component
public class RegistryListener implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ConfigurableApplicationContext configurableApplicationContext = (ConfigurableApplicationContext)applicationContext;
configurableApplicationContext.addApplicationListener(new BaseListener());
configurableApplicationContext.addApplicationListener(new StartListener());
}
}
再定义一个事件发布器,另外,也可以通过applicationContext上下文来直接发布事件
public class Send implements ApplicationEventPublisherAware {
public ApplicationEventPublisher publisher;
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
public void publish(ApplicationEvent event){
publisher.publishEvent(event);
}
public void publish(Object event){
publisher.publishEvent(event);
}
}
这样,在我们的容器启动起来,执行start().或者发送CreateManger事件,就会在对应的监听器中监听变化
public static void main(String[] args) throws IOException, InterruptedException {
AnnotationConfigApplicationContext applicationContext
= new AnnotationConfigApplicationContext("con.note.config");
applicationContext.start();
Send send = applicationContext.getBean(Send.class);
CreateManger createManger = new CreateManger(applicationContext,"mimi");
send.publish(createManger);
}
事件同样也支持异步的操作,但是在我们直接的编码中,这种会稍微麻烦一点,我这边的实现是在 SimpleApplicationEventMulticaster 对象生成后,对其的 TaskExecutor 进行注入,但是这种方式的话,会将所有的事件都变为异步,具体下面的源码解析会说到
@Component
public class SimpleApplicationEventMulticasterAware implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof SimpleApplicationEventMulticaster){
SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = (SimpleApplicationEventMulticaster)bean;
simpleApplicationEventMulticaster.setTaskExecutor(Executors.newFixedThreadPool(10));
return simpleApplicationEventMulticaster;
}
return bean;
}
}
使用注解来使用事件监听
使用注解来监听的话,整体流程相对于我们直接使用ApplicationEvent会方便一些,使用注解的话,我们不但可以监听继承于ApplicationEvent的对象,也可以监听任意Object对象
其主要通过 @EventListener 注解来监听事件,其原理下面会说到。其包括监听的对象,以及 condition,一个条件,对我们监听的事件进行过滤,其支持spel表达式。代码(4)
其中如果需要异步执行的话,只需要在方法加上@Async注解即可。(需要配合@EnableAsync使用)
对于多个监听器,可以使用 @Order(42) 来定义顺序
如果我们的方法是拥有返回值的,那么我们的当前事件处理完成之后,就会发布另一个事件,代码(3)
@EventListener(value = {ContextClosedEvent.class, ContextStoppedEvent.class})
public void springListener(ApplicationContextEvent applicationContextEvent){
ApplicationContext applicationContext = applicationContextEvent.getApplicationContext();
SimpleApplicationEventMulticaster bean = applicationContext.getBean(SimpleApplicationEventMulticaster.class);
}
@Async
@Order(42)
@EventListener(value = {CreateManger.class})
public void baseListener(CreateManger createManger) throws InterruptedException {
Thread.sleep(1000L);
System.out.println("createManger--->"+createManger.getTimestamp());
}
(3)
//指定完了CreateManger 然后执行BuyManger
@EventListener(value = {CreateManger.class})
public BuyManger baseListener(CreateManger createManger) throws InterruptedException {
Thread.sleep(1000L);
System.out.println("createManger--->"+createManger.getTimestamp());
return new BuyManger("测试");
}
(4)其表示事件的属性等于test的时候,才会去执行
@EventListener(value = {CreateManger.class},condition = "#createManger.data == 'test'")
public BuyManger baseListener(CreateManger createManger) throws InterruptedException {
Thread.sleep(1000L);
System.out.println("createManger--->"+createManger.getTimestamp());
return new BuyManger("测试");
}
源码解析
注册事件篇
使用ApplicationListener注册
使用@EventListener注解注册
使用注解注册的时候,Spring会把方法包装成为ApplicationListenerMethodAdapter对象,然后注册在spring中
- 在EventListenerMethodProcessor 对象中
这个对象实现了 SmartInitializingSingleton 接口,其中的 afterSingletonsInstantiated()方法;是在所有bean初始化完成之后调用的,
其中的核心方法在于 processBean,最终在这个方法,将方法信息,源配置类,包装成 ApplicationListenerMethodAdapter 对象,在之后通过反射进行调用
同时,最终也是通过 context.addApplicationListener()注册到上下文中
private void processBean(final String beanName, final Class<?> targetType) {
if (!this.nonAnnotatedClasses.contains(targetType) &&
AnnotationUtils.isCandidateClass(targetType, EventListener.class) &&
!isSpringContainerClass(targetType)) {
Map<Method, EventListener> annotatedMethods = null;
try {
annotatedMethods = MethodIntrospector.selectMethods(targetType,
(MethodIntrospector.MetadataLookup<EventListener>) method ->
AnnotatedElementUtils.findMergedAnnotation(method, EventListener.class));
}
catch (Throwable ex) {...}
else {
// Non-empty set of methods
ConfigurableApplicationContext context = this.applicationContext;
Assert.state(context != null, "No ApplicationContext set");
List<EventListenerFactory> factories = this.eventListenerFactories;
Assert.state(factories != null, "EventListenerFactory List not initialized");
for (Method method : annotatedMethods.keySet()) {
for (EventListenerFactory factory : factories) {
if (factory.supportsMethod(method)) {
Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
//最终在这个方法,将方法信息,源配置类,包装成 ApplicationListenerMethodAdapter 对象,在之后通过反射进行调用
ApplicationListener<?> applicationListener =
factory.createApplicationListener(beanName, targetType, methodToUse);
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
}
context.addApplicationListener(applicationListener);
break;
}
}
}
if (logger.isDebugEnabled()) {
logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
beanName + "': " + annotatedMethods);
}
}
}
}
ApplicationListenerMethodAdapter的构造函数中可以看到,将@EventListener的注解中的 condition,order,参数 等等都封装起来,供事件触发调用
public ApplicationListenerMethodAdapter(String beanName, Class<?> targetClass, Method method) {
this.beanName = beanName;
this.method = BridgeMethodResolver.findBridgedMethod(method);
this.targetMethod = (!Proxy.isProxyClass(targetClass) ?
AopUtils.getMostSpecificMethod(method, targetClass) : this.method);
this.methodKey = new AnnotatedElementKey(this.targetMethod, targetClass);
EventListener ann = AnnotatedElementUtils.findMergedAnnotation(this.targetMethod, EventListener.class);
this.declaredEventTypes = resolveDeclaredEventTypes(method, ann);
this.condition = (ann != null ? ann.condition() : null);
this.order = resolveOrder(this.targetMethod);
}
publishEvent的实现
- 首先判断事件的类型是直接继承ApplicationEvent的,还是Object类型的事件, 上面说过,事件可以直接发布对象
- 在这里会做一个判断,如果是在监听器初始化完成之前发布的事件,就在这里先放入earlyApplicationEvents中,如果初始化完成的,就直接调用multicastEvent来执行
- 如果当前命名空间还有父亲节点,也需要给父亲推送该消息
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
1. 首先判断事件的类型是直接继承ApplicationEvent的,还是Object类型的事件, 上面说过,事件可以直接发布对象
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent<?>) applicationEvent).getResolvableType();
}
}
// Multicast right now if possible - or lazily once the multicaster is initialized
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
// Publish event via parent context as well...
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
multicastEvent
这里就是之前异步的时候配置的线程池的用处
@Override
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
Executor executor = getTaskExecutor();
for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
if (executor != null) {
//异步执行
executor.execute(() -> invokeListener(listener, event));
}
else {
//同步执行
invokeListener(listener, event);
}
}
}
invokeListener#doInvokeListener
最终 是通过各个监听器的onApplicationEvent 来执行事件监听
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
....
}
}
而注解配置的监听器最终通过反射来执行方法
public void processEvent(ApplicationEvent event) {
Object[] args = resolveArguments(event);
if (shouldHandle(event, args)) {
//最终这里通过代理执行事件方法
Object result = doInvoke(args);
if (result != null) {
如果返回值不为空,判断是否是需要再次发布事件,就是方法的返回值的用处
handleResult(result);
}
else {
logger.trace("No result object given - no result to handle");
}
}
}
其他高级监听器
SmartApplicationListener
实现了order的方法
spring文档位置:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#spring-core
1.15.2. Standard and Custom Events
网友评论