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接口实现监听器,GenericApplicationListenerSmartApplicationListener是对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();
            }
        }
    }
}

在这里插入图片描述
可以看到监听器的异步执行,是另起一个线程。


版权声明:本文为yu_kang原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。