@EventListener注解详细使用(IT枫斗者)

@EventListener注解详细使用

简介

  • @EventListener是一种事件驱动编程在spring4.2的时候开始有的,早期可以实现ApplicationListener接口, 为我们提供的一个事件监听、订阅的实现,内部实现原理是观察者设计模式;为的就是业务系统逻辑的解耦,提高可扩展性以及可维护性。事件发布者并不需要考虑谁去监听,监听具体的实现内容是什么,发布者的工作只是为了发布事件而已。
  • 比如我们做一个电商系统,用户下单支付成功后,我们一般要发短信或者邮箱给用户提示什么的,这时候就可以把这个通知业务做成一个单独事件监听,等待通知就可以了;把它解耦处理。

使用@EventListener注解

  • 建立事件对象,当调用publishEvent方法是会通过这个bean对象找对应事件的监听。AddDataEvent.java

  • package com.rw.article.pay.event.bean;
    
    import org.springframework.context.ApplicationEvent;
    
    /**
     * 新增mongodb数据事件
     */
    public class AddDataEvent extends ApplicationEvent {
    
        public AddDataEvent(Object source) {
            super(source);
        }
        public AddDataEvent(Object source, Class clz, Object data) {
            super(source);
            this.clz = clz;
            this.data = data;
        }
    
        public AddDataEvent(Object source, Class clz, Object data, String modelName, String userAgent) {
            super(source);
            this.clz = clz;
            this.data = data;
            this.modelName = modelName;
            this.userAgent = userAgent;
        }
    
    
    
        /** 要更新的表对象 **/
        private Class clz;
    
        /** 操作的数据**/
        private Object data;
    
    
        /** 模块名称**/
        private String modelName;
    
        /** 浏览器标识 **/
        private String userAgent;
    
    
        public Class getClz() {
            return clz;
        }
    
        public void setClz(Class clz) {
            this.clz = clz;
        }
    
        public Object getData() {
            return data;
        }
    
        public void setData(Object data) {
            this.data = data;
        }
    
        public String getModelName() {
            return modelName;
        }
    
        public void setModelName(String modelName) {
            this.modelName = modelName;
        }
    
        public String getUserAgent() {
            return userAgent;
        }
    
        public void setUserAgent(String userAgent) {
            this.userAgent = userAgent;
        }
    }
    
    
    
  • 对应的监听AddDataEventListener .java

  • package com.rw.article.pay.event.listener;
    import com.alibaba.fastjson.JSON;
    import com.rw.article.pay.event.bean.AddDataEvent;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import org.springframework.context.event.EventListener;
    import org.springframework.stereotype.Component;
    
    /**
     * 新增数据的事件监听
     */
    @Component
    public class AddDataEventListener {
        private static Logger log = LoggerFactory.getLogger(AddDataEventListener.class);
    
        /*
        * 在AnnotationConfigUtils#registerAnnotationConfigProcessors注册了BeanDefinition 对应的是EventListenerMethodProcessor对象   , AnnotationConfigUtils在AnnotationConfigServletWebServerApplicationContext构造方法里被加载
        * */
    
     	/**
    	 * DefaultListableBeanFactory#中preInstantiateSingletons -> (beanName为org.springframework.context.event.internalEventListenerProcessor时得到EventListenerMethodProcessor)EventListenerMethodProcessor#afterSingletonsInstantiated this.processBean(factories, beanName, type)
    	 * 然后把要执行的方法封装为ApplicationListenerMethodAdapter -> 添加到listener中 AbstractApplicationEventMulticaster#addApplicationListener
    	 * */
     	// 该方法在 ApplicationListenerMethodAdapter 利用反射执行
        /**
         * 处理新增数据的事件
         **/
        @EventListener
        public void handleAddEvent(AddDataEvent event) {
            log.info("发布的data为:{}  ", JSON.toJSONString(event));
            
        }
    }
    
  • 建立测试类

  • package com.rw.article.pay.action;
    
    import com.rw.article.pay.event.bean.AddDataEvent;
    import org.springframework.context.ApplicationContext;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;
    
    import javax.annotation.Resource;
    
    /**
     * 测试的controller
     */
    @Controller
    @RequestMapping("/test")
    public class TestController {
    
    
       @Resource
       private ApplicationContext applicationContext;
    
    
       @ResponseBody
       @RequestMapping("/testListener")
       public String testListener(){
          applicationContext.publishEvent(new AddDataEvent(this,TestController.class,"test"));
          return "success";
       }
    }
    
  • 结果是能够监听到的

  • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pHjkZeUm-1680567653256)(C:%5CUsers%5Cquyanliang%5CAppData%5CRoaming%5CTypora%5Ctypora-user-images%5C1680567079331.png)]

  • 如果要使用异步加上@EnableAsync注解,方法上加@Async注解,如下spring boot项目配置

  • @SpringBootApplication
    @EnableAsync
    public class XApplication{
        public static void main(String[] args) {
            ConfigurableApplicationContext run = new SpringApplicationBuilder(XApplication.class).web(true).run(args);
            run.publishEvent("test");
        }
    }
    
  •     @Async
        @EventListener
        public void test(String wrapped){
            System.out.println("当前线程 "+Thread.currentThread().getName());
            System.out.println(wrapped);
        }
    
  • 还可以配置线程池taskExecutor

  • @Configuration
    public class GenericConfiguration {
    
        @Bean
        public Executor taskExecutor() {
            ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            //核心线程数:线程池创建时候初始化的线程数
            //最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
            //缓冲队列:用来缓冲执行任务的队列
            //允许线程的空闲时间60秒:当超过了核心线程出之外的线程在空闲时间到达之后会被销毁
            //线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
            //线程池对拒绝任务的处理策略:这里采用了CallerRunsPolicy策略,当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
            executor.setCorePoolSize(5);
            executor.setMaxPoolSize(10);
            executor.setQueueCapacity(20);
            executor.setKeepAliveSeconds(60);
            executor.setThreadNamePrefix("taskExecutor-");
            executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
            return executor;
        }
    }
    

源码解析

  • 原理还得从org.springframework.context.event.internalEventListenerProcessor
    说起。

  • 在AnnotationConfigUtils#registerAnnotationConfigProcessors注册了BeanDefinition 对应的是EventListenerMethodProcessor对象 , 而AnnotationConfigUtils是在AnnotationConfigServletWebServerApplicationContext构造方法里被加载。这里要提一下AnnotationConfigServletWebServerApplicationContext,他是spring boot启动入口的重要类(我这里用的是spring boot所以是这个类),可以相当于以前用xml的ClassPathXmlApplicationContext。

  •  public static final String EVENT_LISTENER_PROCESSOR_BEAN_NAME =
          "org.springframework.context.event.internalEventListenerProcessor";
    
    public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(
    			BeanDefinitionRegistry registry, @Nullable Object source) {
    
    		 ................... 
    
    		// 注册EventListenerMethodProcessor对象
    		if (!registry.containsBeanDefinition(EVENT_LISTENER_PROCESSOR_BEAN_NAME)) {
    			RootBeanDefinition def = new RootBeanDefinition(EventListenerMethodProcessor.class);
    			def.setSource(source);
    			beanDefs.add(registerPostProcessor(registry, def, EVENT_LISTENER_PROCESSOR_BEAN_NAME));
    		}
    		...........................
    
    		return beanDefs;
    	}
    
  • 注册的EventListenerMethodProcessor对象会在初始化非懒加载对象的时候运行它的afterSingletonsInstantiated方法。
    AbstractApplicationContext#finishBeanFactoryInitialization

  •  protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
     
       ............. 
       // 初始化非懒加载对象
       beanFactory.preInstantiateSingletons();
    }
    
  • DefaultListableBeanFactory#preInstantiateSingletons

  •  @Override
    public void preInstantiateSingletons() throws BeansException {
        ..................
    
       // 触发所有适用bean的初始化后回调 主要是afterSingletonsInstantiated方法
       for (String beanName : beanNames) {
    //如果beanName传入org.springframework.context.event.internalEventListenerProcessor 因为已经上面代码已经初始化,将从缓存中得到一个EventListenerMethodProcessor对象
          Object singletonInstance = getSingleton(beanName);
          if (singletonInstance instanceof SmartInitializingSingleton) {
             final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
             if (System.getSecurityManager() != null) {
                AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
                   smartSingleton.afterSingletonsInstantiated();
                   return null;
                }, getAccessControlContext());
             }
             else {
    // 调用其afterSingletonsInstantiated方法
                smartSingleton.afterSingletonsInstantiated();
             }
          }
       }
    }
    
  • EventListenerMethodProcessor#afterSingletonsInstantiated

  •  @Override
    public void afterSingletonsInstantiated() {
       List<EventListenerFactory> factories = getEventListenerFactories();
       ConfigurableApplicationContext context = getApplicationContext();
       String[] beanNames = context.getBeanNamesForType(Object.class);
       for (String beanName : beanNames) {
          if (!ScopedProxyUtils.isScopedTarget(beanName)) {
             Class<?> type = null;
             try {
                type = AutoProxyUtils.determineTargetClass(context.getBeanFactory(), 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(
                            context.getBeanFactory(), 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 {
    		// 重点是这个方法 处理bean
                   processBean(factories, beanName, type);
                }
                catch (Throwable ex) {
                   throw new BeanInitializationException("Failed to process @EventListener " +
                         "annotation on bean with name '" + beanName + "'", ex);
                }
             }
          }
       }
    }
    
  • EventListenerMethodProcessor#processBean;这里有一个重要的类就是ApplicationListenerMethodAdapter,spring把加入了@EventListener注解的方法封装进ApplicationListenerMethodAdapter对象里,然后我们publishEvent方法是,其实是调用的对应的ApplicationListenerMethodAdapter,然后里面是执行这个方法,这里可以看下ApplicationListenerMethodAdapter类的属性。

  • public class ApplicationListenerMethodAdapter implements GenericApplicationListener {
    
       protected final Log logger = LogFactory.getLog(getClass());
    
       private final String beanName;
    
       private final Method method;
    
       private final Method targetMethod;
    
       private final AnnotatedElementKey methodKey;
    
       private final List<ResolvableType> declaredEventTypes;
    
       @Nullable
       private final String condition;
    
       private final int order;
    
       @Nullable
       private ApplicationContext applicationContext;
    
       @Nullable
       private EventExpressionEvaluator evaluator;
    	..................................
    }
    
  • protected void processBean(
          final List<EventListenerFactory> factories, final String beanName, final Class<?> targetType) {
    
       if (!this.nonAnnotatedClasses.contains(targetType)) {
          Map<Method, EventListener> annotatedMethods = null;
          try {
             // 拿到使用了@EventListener注解的方法
             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 = getApplicationContext();
             for (Method method : annotatedMethods.keySet()) {
                for (EventListenerFactory factory : factories) {
                   // 判断是否支持该方法  这里用的DefaultEventListenerFactory spring5.0.8 写死的返回true
                   if (factory.supportsMethod(method)) {
                      //选择方法  beanName 这里是AddDataEventListener的beanName 默认是addDataEventListener
                      Method methodToUse = AopUtils.selectInvocableMethod(method, context.getType(beanName));
                      // 这里是创建一个ApplicationListenerMethodAdapter对象
                      ApplicationListener<?> applicationListener =
                            factory.createApplicationListener(beanName, targetType, methodToUse);
                      if (applicationListener instanceof ApplicationListenerMethodAdapter) {
                         // 如果是ApplicationListenerMethodAdapter对象 就把context和evaluator传进去
                         ((ApplicationListenerMethodAdapter) applicationListener).init(context, this.evaluator);
                      }
    
                      // 添加到ApplicationListener事件Set集合中去
                      context.addApplicationListener(applicationListener);
                      break;
                   }
                }
             }
             if (logger.isDebugEnabled()) {
                logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" +
                      beanName + "': " + annotatedMethods);
             }
          }
       }
    }
    
  • 后面就是触发事件监听了AbstractApplicationContext#publishEvent

  •  @Override
    public void publishEvent(ApplicationEvent event) {
       publishEvent(event, null);
    }
    
    
    protected void publishEvent(Object event, @Nullable ResolvableType eventType) {
    	 
    			..............................
    		// Multicast right now if possible - or lazily once the multicaster is initialized
    		if (this.earlyApplicationEvents != null) {
    			this.earlyApplicationEvents.add(applicationEvent);
    		}
    		else {
    		    // 进入multicastEvent
    			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->invokeListener->doInvokeListener

  • 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().getName())) {
             // Possibly a lambda-defined listener which we could not resolve the generic event type for
             // -> let's suppress the exception and just log a debug message.
             Log logger = LogFactory.getLog(getClass());
             if (logger.isDebugEnabled()) {
                logger.debug("Non-matching event type for listener: " + listener, ex);
             }
          }
          else {
             throw ex;
          }
       }
    }
    
  • ApplicationListenerMethodAdapter#onApplicationEvent]

  • @Override
    public void onApplicationEvent(ApplicationEvent event) {
       processEvent(event);
    }
    ApplicationListenerMethodAdapter#processEvent
     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");
          }
       }
    }
    
  • ApplicationListenerMethodAdapter#doInvoke

  •  protected Object doInvoke(Object... args) {
       Object bean = getTargetBean();
       ReflectionUtils.makeAccessible(this.method);
       try {
          return this.method.invoke(bean, args);
       }
       catch (IllegalArgumentException ex) {
          assertTargetBean(this.method, bean, args);
          throw new IllegalStateException(getInvocationErrorMessage(bean, ex.getMessage(), args), ex);
       }
       catch (IllegalAccessException ex) {
          throw new IllegalStateException(getInvocationErrorMessage(bean, ex.getMessage(), args), ex);
       }
       catch (InvocationTargetException ex) {
          // Throw underlying exception
          Throwable targetException = ex.getTargetException();
          if (targetException instanceof RuntimeException) {
             throw (RuntimeException) targetException;
          }
          else {
             String msg = getInvocationErrorMessage(bean, "Failed to invoke event listener method", args);
             throw new UndeclaredThrowableException(targetException, msg);
          }
       }
    }
    
  • ApplicationListenerMethodAdapter#getTargetBean

  •  protected Object getTargetBean() {
       Assert.notNull(this.applicationContext, "ApplicationContext must no be null");
       return this.applicationContext.getBean(this.beanName);
    }
    

猜你喜欢

转载自blog.csdn.net/Andrew_Chenwq/article/details/129943960
今日推荐