0 概述
本文将分别从使用、源码层面上,来分析Spring 的ApplicationContext事件机制。。
1 首先来看如何使用
//获取上下文类ApplicationContext,通过ApplicationContext发布事件
@Component
public class SpringContext implements ApplicationContextAware{
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringContext.applicationContext=applicationContext;
}
public static ApplicationContext getApplicationContext(){
return applicationContext;
}
}
//事件类
import org.springframework.context.ApplicationEvent;
public class TestEvent extends ApplicationEvent {
private String data;
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public TestEvent(Object source) {
super(source);
}
}
//监听器监听某个事件类
@Component
public class TestEventListener implements ApplicationListener<TestEvent> {
@Override
public void onApplicationEvent(TestEvent event) {
System.out.println("---event.getData()---"+event.getData());
}
}
测试入口类
import com.hsc.study.context.SpringContext;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
* Created by apple on 17/12/2.
*/
public class Test {
public static void main(String[] args) throws Exception{
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
TestEvent testEvent=new TestEvent(new Object());
testEvent.setData("testEvent");
//发布事件 SpringContext.getApplicationContext().publishEvent(testEvent);
//主程序等会退出
Thread.sleep(10000);
}
}
1.事件是怎么发出去的?
2.事件怎么找到具体监听器的?
3.是同步还是异步的?
2 原理分析
从代码上可以看出,1)ApplicationContext会从容器中找出符合的Listener(根据事件类的类型找到符合条件的Listener,可能是多个),由此也可以看出Listener必须是bean;2)默认是同步 调用Listener。
3 如何实现异步方式调用
从SimpleApplicationEventMulticaster类中代码可以看出,如果想做成异步需要给出线程池。
@Override
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
//返回executor(线程池)不为空,将invokeListener放到线程池中去做(异步方式)
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
invokeListener(listener, event);
}
});
}
else {
invokeListener(listener, event);
}
}
}
实现异步方式,值得说明是这个bean name 必须为applicationEventMulticaster。
import org.springframework.context.event.SimpleApplicationEventMulticaster;
import org.springframework.stereotype.Component;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* Created by apple on 18/1/8.
* bean 名字必须为applicationEventMulticaster
*/
@Component("applicationEventMulticaster")
public class ApplicationEventMulticaster extends SimpleApplicationEventMulticaster {
private Executor executor=Executors.newFixedThreadPool(20);
@Override
protected Executor getTaskExecutor() {
return executor;
}
}
为什么bean name 必须指定为applicationEventMulticaster,从AbstractApplicationContext中代码可以看出,在初始化ApplicationEventMulticaster时候会检查bean 是否存在name为applicationEventMulticaster的bean,如果存在就获取当前bean,如果不存在则new 一个SimpleApplicationEventMulticaster并注册到beanFactory。
public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
4 总结
通过ApplicationContext事件可以将一些耗时且不重要的操作异步来做而且可以将代码解耦。