前言
Spring事件监听机制,通过ApplicationEvent以及Listener提供事件的订阅与处理,其内部是基于观察者模式的应用。当定义的某个事件被广播出去后,所有订阅该事件的Listener都会被通知而出发相应处理,该模式可运用与业务逻辑间解耦,提高系统可扩展性与可维护性。例如我们在完登录成功后需要发送短信通知,那么可以通过发布异步事件的方式触发短信相关业务。
使用
定义事件
public class LoginEvent extends ApplicationEvent {
public LoginEvent(Object source) {
super(source);
}
}
定义监听
@Service
public class BussinessEventListener implements ApplicationListener{
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("发送邮件...."+event.getSource());
}
}
实现接口代码侵入性较强,所以我们可以使用@EventListener注解的方式可以定义多个事件监听
@Component
public class BussinessEventListener{
@EventListener
public void sendMessage1(UserEvent event){
System.out.println("sendMessage1 发送邮件...."+event.getSource());
}
/**
* 这种方式可以监听多个事件类型
*/
@EventListener({UserEvent.class})
public void sendMessage2(ApplicationEvent event){
System.out.println("sendMessage2 发送邮件...."+event.getSource());
}
}
发布事件
我们可以通过注入applicationContext或者ApplicationEventPublisher来发布事件,继而触发对应监听方法中的业务。
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private ApplicationContext applicationContext;
@GetMapping(value = "login")
public void login(String key) {
System.out.println("处理用户登录业务.....");
//发布通知
applicationContext.publishEvent(new UserEvent(key));
}
}
当然更多的时候我们是通过工具类的方式调用,例如自定义一个SpringUtils
@Component
public class SpringUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
SpringUtils.applicationContext = applicationContext;
}
public static void publish(ApplicationEvent event) {
applicationContext.publishEvent(event);
}
...
}
@RestController
@RequestMapping("/user")
public class UserController {
@GetMapping(value = "login")
public void login(String key) {
System.out.println("处理用户登录业务.....");
//发布通知
SpringUtils.publish(new UserEvent(key));
}
}
异步配置
在用户登录逻辑执行完毕后调用邮件通知,同时可以通过启动类添加@EnableAsync,并在监听方法上加@Async开启异步消息通知
@EnableAsync
@SpringBootApplication
public class PandaSpringbootEventlisterDemoApplication {
public static void main(String[] args) {
SpringApplication.run(PandaSpringbootEventlisterDemoApplication.class, args);
}
}
@Component
public class BussinessEventListener{
@Async//异步执行
@EventListener
public void sendMessage1(UserEvent event){
System.out.println("sendMessage1 发送邮件...."+event.getSource());
}
}
原理分析
首先看一下事件是如何发布出去的,进入 applicationContext.publishEvent方法
@Override
public void publishEvent(ApplicationEvent event) {
publishEvent(event, null);
}
protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
// Decorate event as an ApplicationEvent if necessary
ApplicationEvent applicationEvent;
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
//如果发布的不是ApplicationEvent事件,而是一个对象,通过PayloadApplicationEvent进行包装
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);
}
}
}
可以看到这里通过SimpleApplicationEventMulticaster类multicastEvent进行多路事件发布,获取容器中对应事件类型的ApplicationListener的bean,并调用onApplicationEvent方法
@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);
}
}
}
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);
}
}
private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {
try {
listener.onApplicationEvent(event);
}
catch (ClassCastException ex) {
String msg = ex.getMessage();
if (msg == null || matchesClassCastMessage(msg, event.getClass()) ||
(event instanceof PayloadApplicationEvent &&
matchesClassCastMessage(msg, ((PayloadApplicationEvent) event).getPayload().getClass()))) {
// Possibly a lambda-defined listener which we could not resolve the generic event type for
// -> let's suppress the exception.
Log loggerToUse = this.lazyLogger;
if (loggerToUse == null) {
loggerToUse = LogFactory.getLog(getClass());
this.lazyLogger = loggerToUse;
}
if (loggerToUse.isTraceEnabled()) {
loggerToUse.trace("Non-matching event type for listener: " + listener, ex);
}
}
else {
throw ex;
}
}
}
SimpleApplicationEventMulticaste
spring是通过SimpleApplicationEventMulticaster来进行多路事件广播,那么这个组件是什么时候注入容器中的呢?
我们关注IOC容器初始化的时调用refresh方法
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// Initialize message source for this context.
initMessageSource();
// 初始化多路事件广播器
// Initialize event multicaster for this context.
initApplicationEventMulticaster();
// Initialize other special beans in specific context subclasses.
onRefresh();
// Check for listener beans and register them.
registerListeners();
// Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory);
// Last step: publish corresponding event.
finishRefresh();
}
...
}
}
protected void initApplicationEventMulticaster() {
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
this.applicationEventMulticaster =
beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
if (logger.isTraceEnabled()) {
logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
}
}
else {
this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
if (logger.isTraceEnabled()) {
logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
"[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
}
}
}
ApplicationListener
spring在发布消息时,会从SimpleApplicationEventMulticaster中拿出所有的ApplicationListener,那么这些ApplicationListener何时被注入的呢?
继续看AbstractApplicationContext#refresh方法中,会调用registerListeners来注册所有监听类
/**
* Add beans that implement ApplicationListener as listeners.
* Doesn't affect other listeners, which can be added without being beans.
*/
protected void registerListeners() {
// Register statically specified listeners first.
// 内置事件监听 比如ContextStartedEvent ContextStoppedEvent ...
for (ApplicationListener<?> listener : getApplicationListeners()) {
getApplicationEventMulticaster().addApplicationListener(listener);
}
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let post-processors apply to them!
// 自定义监听
String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
for (String listenerBeanName : listenerBeanNames) {
getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
}
// Publish early application events now that we finally have a multicaster...
Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
this.earlyApplicationEvents = null;
if (!CollectionUtils.isEmpty(earlyEventsToProcess)) {
for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
getApplicationEventMulticaster().multicastEvent(earlyEvent);
}
}
}
@EventListener注解的原理
我们使用注解方式实现的监听方法什么时候注入容器呢?
EventListenerMethodProcessor用来解析所有的@EventListener注解,由于实现了SmartInitializingSingleton类,方法afterSingletonsInstantiated会在所有bean初始化后执行,获取所有注解方法,并封装成ApplicationListenerMethodAdapter,执行init方法构造ApplicationListener并注册到容器中
@Override
public void afterSingletonsInstantiated() {
ConfigurableListableBeanFactory beanFactory = this.beanFactory;
Assert.state(this.beanFactory != null, "No ConfigurableListableBeanFactory set");
String[] beanNames = beanFactory.getBeanNamesForType(Object.class);
for (String beanName : beanNames) {
if (!ScopedProxyUtils.isScopedTarget(beanName)) {
Class<?> type = null;
try {
type = AutoProxyUtils.determineTargetClass(beanFactory, beanName);
}
catch (Throwable ex) {
// An unresolvable bean type, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex);
}
}
if (type != null) {
if (ScopedObject.class.isAssignableFrom(type)) {
try {
Class<?> targetClass = AutoProxyUtils.determineTargetClass(
beanFactory, ScopedProxyUtils.getTargetBeanName(beanName));
if (targetClass != null) {
type = targetClass;
}
}
catch (Throwable ex) {
// An invalid scoped proxy arrangement - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex);
}
}
}
try {
processBean(beanName, type);
}
catch (Throwable ex) {
throw new BeanInitializationException("Failed to process @EventListener " +
"annotation on bean with name '" + beanName + "'", ex);
}
}
}
}
}
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) {
// An unresolvable type in a method signature, probably from a lazy bean - let's ignore it.
if (logger.isDebugEnabled()) {
logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex);
}
}
if (CollectionUtils.isEmpty(annotatedMethods)) {
this.nonAnnotatedClasses.add(targetType);
if (logger.isTraceEnabled()) {
logger.trace("No @EventListener annotations found on bean class: " + targetType.getName());
}
}
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));
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);
}
}
}
}