Spring Framework之监听器的使用
spring的事件
spring默认的事件
ContextRefreshedEvent
容器完成初始化后调用finishRefresh()时会发布ContextRefreshedEvent事件
protected void finishRefresh() {
this.clearResourceCaches();
this.initLifecycleProcessor();
this.getLifecycleProcessor().onRefresh();
this.publishEvent((ApplicationEvent)(new ContextRefreshedEvent(this)));
LiveBeansView.registerApplicationContext(this);
}
ContextStartedEvent
需要手动调用start()方法,发布ContextStartedEvent事件
public void start() {
this.getLifecycleProcessor().start();
this.publishEvent((ApplicationEvent)(new ContextStartedEvent(this)));
}
ContextStoppedEvent
需要手动调用stop()方法,发布ContextStoppedEvent事件
public void stop() {
this.getLifecycleProcessor().stop();
this.publishEvent((ApplicationEvent)(new ContextStoppedEvent(this)));
}
ContextClosedEvent
需要手动调用close()或destroy()方法时,发布ContextClosedEvent事件
public void stop() {
this.getLifecycleProcessor().stop();
this.publishEvent((ApplicationEvent)(new ContextStoppedEvent(this)));
}
spring事件接口
ApplicationContextEvent 与 ApplicationEvent
造轮子得先看轮子长啥样,先看看spring默认的事件怎么写的
ContextClosedEvent.java
public class ContextClosedEvent extends ApplicationContextEvent {
public ContextClosedEvent(ApplicationContext source) {
super(source);
}
}
ApplicationContextEvent.java
public abstract class ApplicationContextEvent extends ApplicationEvent {
public ApplicationContextEvent(ApplicationContext source) {
super(source);
}
public final ApplicationContext getApplicationContext() {
return (ApplicationContext)this.getSource();
}
}
ApplicationEvent.java
public abstract class ApplicationEvent extends EventObject {
private static final long serialVersionUID = 7099057708183571937L;
private final long timestamp = System.currentTimeMillis();
public ApplicationEvent(Object source) {
super(source);
}
public final long getTimestamp() {
return this.timestamp;
}
}
发现事件最终是EventObject,但是spring的监听器监听的事件类型是ApplicationEvent,所以我们可以通过实现ApplicationContextEvent 或ApplicationEvent 实现自定义监听器。
看构造函数发现:
- 实现
ApplicationContextEvent要求传入的事件源是ApplicationContext类型 - 实现
ApplicationEvent就很灵活,只需传入Object类型
自定义spring事件
MyEvent.java
public class MyEvent extends ApplicationEvent {
public MyEvent(Object source) {
super(source);
}
}
发布事件
发布自定义事件
执行发送邮件方法时发布该事件
@Service
public class EmailService {
@Autowired
private ApplicationContext applicationContext;
/**
* 发送邮件时发布事件
*/
public void sendEmail(){
System.out.println(Thread.currentThread().getName());
System.out.println("first eamil send success.");
// 发布事件
applicationContext.publishEvent(new MyEvent(applicationContext));
System.out.println("second eamil send callBack.");
}
}
发布事件的源码跟踪
AbstractApplicationContext.java
public void publishEvent(Object event) {
this.publishEvent(event, (ResolvableType)null);
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<>(this, event);
if (eventType == null) {
eventType = ((PayloadApplicationEvent) applicationEvent).getResolvableType();
}
}
if (this.earlyApplicationEvents != null) {
this.earlyApplicationEvents.add(applicationEvent);
}
else {
// 调用SimpleApplicationEventMulticaster的方法
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
}
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}
其中getApplicationEventMulticaster()就是返回SimpleApplicationEventMulticaster,在refresh()方法中调用this.initApplicationEventMulticaster();时初始化的spring加载流程refresh之initApplicationEventMulticaster()
SimpleApplicationEventMulticaster.java
获取所有已注册的监听器spring加载流程refresh之registerListeners(),判断出符合条件的监听器
看到这里有个判断是否有多线程,线程是异步的,也就是说监听器可以异步执行。
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
// 根据事件与事件类型获取监听器(监听器都会绑定一个事件)
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
Executor executor = getTaskExecutor();
// 判断是否有线程池
if (executor != null) {
executor.execute(() -> invokeListener(listener, event));
}
else {
invokeListener(listener, event);
}
}
}
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {
// 判断是否有错误处理器,定义监听器出错时的处理方式
ErrorHandler errorHandler = getErrorHandler();
if (errorHandler != null) {
try {
doInvokeListener(listener, event);
}
catch (Throwable err) {
errorHandler.handleError(err);
}
}
else {
doInvokeListener(listener, event);
}
}
最后会执行listener.onApplicationEvent(event);,就是我们在上面定义的监听动作。
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
// 执行监听器重写的方法
listener.onApplicationEvent(event);
} catch (ClassCastException var6) {
String msg = var6.getMessage();
if (msg != null && !msg.startsWith(event.getClass().getName())) {
throw var6;
}
Log logger = LogFactory.getLog(this.getClass());
if (logger.isDebugEnabled()) {
logger.debug("Non-matching event type for listener: " + listener, var6);
}
}
}
spring的监听器
String[] listenerBeanNames = this.getBeanNamesForType(ApplicationListener.class, true, false);
接口编程式实现
spring通过ApplicationListener接口实现监听器,GenericApplicationListener与SmartApplicationListener是对ApplicationListener的封装,实现了排序功能。会在执行spring加载流程refresh之registerListeners()的时候注册此方式实现的监听器
实现ApplicationListener
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
* 执行监听动作
*/
public void onApplicationEvent(ApplicationEvent event) {
// 监听发送邮件事件
if(event instanceof MyEvent){
System.out.println(Thread.currentThread().getName());
}
}
}
实现GenericApplicationListener
@Component
public class MyGenericApplicationListener implements GenericApplicationListener{
/**
* 匹配感兴趣监听器
* @param resolvableType
* @return
*/
@Override
public boolean supportsEventType(ResolvableType resolvableType) {
//if (resolvableType.getType() == ContextRefreshedEvent.class)
// return true;
return false;
}
/**
* aClass事件源,spring内部默认的事件源都是AnnotationApplicationContext
* @param aClass
* @return
*/
@Override
public boolean supportsSourceType(@Nullable Class<?> aClass) {
return true;
}
/**
* 执行监听动作
* @param applicationEvent
*/
@Override
public void onApplicationEvent(ApplicationEvent applicationEvent) {
System.out.println("applicationEvent="+applicationEvent);
}
/**
* 排序
* @return
*/
@Override
public int getOrder() {
return 0;
}
}
实现SmartApplicationListener
与实现GenericApplicationListener几乎一样
@Component
public class MySmartApplicationListener implements SmartApplicationListener {
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
//if(ContextRefreshedEvent.class == aClass)
// return true;
return true;
}
@Override
public boolean supportsSourceType(@Nullable Class<?> aClass) {
return false;
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
}
@Override
public int getOrder() {
return 0;
}
}
@EventListener注解式实现
@Component
public class AnnotationListener {
@EventListener(classes={ApplicationEvent.class}) //classes 具体的事件
public void listen1(ApplicationEvent event){
System.out.println("listen1...监听到的事件:"+event);
}
@EventListener(classes={ApplicationEvent.class}) //classes 具体的事件
public void listen2(ApplicationEvent event){
System.out.println("listen2...监听到的事件:"+event);
}
}
提示 此种方式自定义的监听器的bd,sping在实例化bd的时候会去判断注解@EventListener,并注册为监听器。具体由注册EventListenerMethodProcessor处理
监听器同步与异步
spring监听器默认是同步触发,要等待监听器执行完后才返回。
例如发送邮件发布一个事件MyEvent,监听器MyApplicationListener监听到该事件,会影响发送邮件方法后面的执行流程,此时可以使监听器异步执行。
@Async异步调用
@Async是spring的注解,需要在配置类用@EnableAsync激活才有效
重新编写监听器MyApplicationListener
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
/**
* 执行监听动作
*/
// spring事件的执行机制默认使用单线程同步执行,异步执行可使用@Async注解实现
@Async
public void onApplicationEvent(ApplicationEvent event) {
// 监听发送邮件事件
if(event instanceof MyEvent){
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(4000);
System.out.println("third do something");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
可以看到监听器的异步执行,是另起一个线程。