Spring中的事件

栏目: Java · 发布时间: 4年前

内容简介:​
  • 学过编程语言的肯定知道事件,在JS中事件,Android中的事件,大多是鼠标点击,键盘事件,手指滑动事件等等。在Spring中也有一些事件,比如容器启动、容器关闭、容器刷新都是一个事件。
  • 既然有了事件,自然少不了事件监听器,事件分发器等,后续会详细介绍

事件

Spring中内置的事件

ContextStartedEvent
ContextRefreshedEvent
ContextStoppedEvent
ContextClosedEvent

自定义事件

  • Spring中自定义事件只需要继承 ApplicationEvent 即可完成一个自定义的Spring事件
    /**
     * 自定义事件,继承ApplicationEvent
     */
    @Data
    public class FirstEventextends ApplicationEvent{
        /**
         * 需要携带的消息,可以是任意类型的数据,相当于传递数据
         */
        private String message;
    
        /**
         * 构造方法
         * @param source 事件发生的类
         * @param message 携带的消息
         */
        public FirstEvent(Object source,String message){
            super(source);
            this.message=message;
        }
    }
    

监听器

  • 监听器用来监听事件触发,一旦事件触发了,监听器会执行相应的操作。
  • 监听器的实现有两种方式:
    ApplicationListener
    @EventListener
    

实现ApplicationListener接口

  • 创建监听器需要两个条件:

    • 实现ApplicationListener接口
    • 将该自定义的监听器注入到ioc容器中
      /**
       * 自定义一个监听器,实现ApplicationListener,指定的泛型就是需要监听的事件
       * 监听ContextRefreshedEvent,当容器完成刷新的时候该监听器就会监听到并执行onApplicationEvent方法
       */
      @Component
      public class FirstListenerimplements ApplicationListener<ContextRefreshedEvent>{
          /**
           * 重载方法,被监听的事件触发了就会调用这个方法
           * @param event 触发事件的对象
           */
          @Override
          public void onApplicationEvent(ContextRefreshedEvent event){
              System.out.println("容器刷新的监听器启动了........");
              System.out.println(event.getSource()+"---->"+event.getTimestamp()+"----"+event.getApplicationContext());
              System.out.println(".........................................");
          }
      }
      
  • 此时只要启动容器,自定义的监听器就会起作用,当然我们监听的是Spring内置的事件,在容器启动的时候Spring会使用事件发布器发布事件,此时才是真正的触发事件,我们自定义的事件并不能被监听,除非被事件发布器发布。

使用@EventListener注解

  • 常见的属性:
    • classes :Class数组,指定需要监听的事件
    • condition :指定条件,默认监听
      /**
       * 注解方式实现事件监听器
       */
      @Component
      public class CustomEventListener{
          /**
           * 使用@EventListener监听事件
           * @param event 传入的事件源
           */
          @EventListener(classes = {ApplicationEvent.class})
          public void handlerContextRefreshEvent(ApplicationEvent event){
              if (event instanceof ContextRefreshedEvent) {
                  ContextRefreshedEvent e=(ContextRefreshedEvent)event;
                  System.out.println("ContextRefreshedEvent启动了........");
                  System.out.println(event.getSource() + "---->" + event.getTimestamp() + "----" + e.getApplicationContext());
                  System.out.println(".........................................");
              }else if(event instanceof ContextStartedEvent){
                  ContextStartedEvent e=(ContextStartedEvent)event;
                  System.out.println("ContextStartedEvent启动了........");
                  System.out.println(event.getSource() + "---->" + event.getTimestamp() + "----" + e.getApplicationContext());
                  System.out.println(".........................................");
              }else if(event instanceof ContextStoppedEvent){
                  ContextStoppedEvent e=(ContextStoppedEvent)event;
                  System.out.println("ContextStoppedEvent启动了........");
                  System.out.println(event.getSource() + "---->" + event.getTimestamp() + "----" + e.getApplicationContext());
                  System.out.println(".........................................");
              }else if(event instanceof ContextClosedEvent){
                  ContextClosedEvent e=(ContextClosedEvent)event;
                  System.out.println("ContextClosedEvent启动了........");
                  System.out.println(event.getSource() + "---->" + event.getTimestamp() + "----" + e.getApplicationContext());
                  System.out.println(".........................................");
              }
          }
      
      
          /**
           * 可以不指定classes,默认监听的是方法参数中的事件
           * @param event 事件源
           */
          @EventListener
          public void handleFirstEvent(FirstEvent event){
              System.out.println("firstEvent事件启动了,。。。。。。。。。");
              System.out.println(event.getSource()+"---->"+event.getMessage());
          }
      }
      

事件发布

  • Spring中发布事件的接口是 ApplicationEventPublisher ,我们可以自定义自己的类,当然也可以使用spring现成的类

Spring的事件发布类

  • ApplicationContext
  • AnnotationConfigApplicationContext

直接注入

  • 在容器启动刷新的时候已经注入了 ApplicationEventPublisher 的实现,我们可以直接注入使用。如下:

    /**
     * 自定义的事件发布器
     */
    @Component
    public class CustomPublisher{
        /**
         * 直接注入ApplicationEventPublisher
         */
        @Autowired
        private ApplicationEventPublisher applicationEventPublisher;
    
        /**
         * 发布事件
         * @param event 指定的事件
         */
        public void publishEvent(ApplicationEvent event){
            applicationEventPublisher.publishEvent(event);
        }
    }
    
  • 测试

    @Test
    public void test1(){
        CustomPublisher customPublisher = applicationContext.getBean(CustomPublisher.class);
        customPublisher.publishEvent(new FirstEvent(this,"启动自定义事件"));
    }
    

使用ApplicationEventPublisherAware注入

/**
 * 自定义的事件发布器,实现ApplicationEventPublisherAware接口
 */
@Component
public class CustomPublisherimplements ApplicationEventPublisherAware{
    private ApplicationEventPublisher applicationEventPublisher;

    /**
     * 发布事件
     * @param event 指定的事件
     */
    public void publishEvent(ApplicationEvent event){
        applicationEventPublisher.publishEvent(event);
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher){
        this.applicationEventPublisher=applicationEventPublisher;
    }
}

事件多播器

  • 何为事件多播器【 ApplicationEventMulticaster 】?
    • 简单的说事件多播器就是一个管理事件监听器并且广播事件【根据指定的事件调用指定的监听器而已】
  • spring中两个实现类分别为 AbstractApplicationEventMulticasterSimpleApplicationEventMulticaster
  • 如何广播事件?【如何通过指定的事件调用指定的监听器】
    • 真正的实现在 org.springframework.context.event.SimpleApplicationEventMulticaster#multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType) 这个方法中,如下:
@Override
	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 executor = getTaskExecutor();
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
                //否则的话,同步执行,调用invokeListener
				invokeListener(listener, event);
			}
		}
	}


/****************************************invokeListener******************************/
protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event){
    	//如果有异常处理器,就try-catch执行
		ErrorHandler errorHandler = getErrorHandler();
		if (errorHandler != null) {
			try {
				doInvokeListener(listener, event);
			}
			catch (Throwable err) {
                //如果有异常了,执行异常处理器的handleError方法
				errorHandler.handleError(err);
			}
		}
		else {
            //没有异常处理器直接执行
			doInvokeListener(listener, event);
		}
	}

/*******************************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())) {
				// 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;
			}
		}
	}

异步事件

  • 前面创建的事件和监听器都是同步进行,我们可以使用异步事件

使用@Async实现异步

  • Spring中可以使用 @Async 注解标注方法异步执行,不过需要在配置类上开启异步功能,使用 @EnableAsync 注解,如下:

    @Configuration
    @ComponentScan(value = {"cn.tedu.demo"})
    @EnableAsync
    public class FirstConfig{
        
    }
    
  • 此时可以在监听方法上标注 @Async 注解,使得事件异步执行

    /**
     * 注解方式实现事件监听器
     */
    @Component
    public class CustomEventListener{
        /**
         * 可以不指定classes,默认监听的是方法参数中的事件
         * @Async : 指定这个方法异步执行
         * @param event 事件源
         */
        @EventListener
        @Async
        public void handleFirstEvent(FirstEvent event){
            System.out.println("firstEvent事件启动了,。。。。。。。。。");
            System.out.println(event.getSource()+"---->"+event.getMessage());
        }
    }
    

自定义事件多播器

  • 从源码我们可以知道,spring容器加载的时候先获取的是ioc容器中的,如果不存在,那么才会新建一个 SimpleApplicationEventMulticaster ,我们可以自己注入一个多播器直接使用即可。
  • 源码如下:

    protected void initApplicationEventMulticaster(){
    		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    		//判断ioc容器中是否存在id为applicationEventMulticaster事件多播器
    		if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
    		    //直接使用ioc容器中的
    			this.applicationEventMulticaster =
    					beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
    			if (logger.isTraceEnabled()) {
    				logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
    			}
    		}
    		else {
    		    //新建一个,不过没有设置TaskExector
    			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() + "]");
    			}
    		}
    	}
    
  • 从源码中【 org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(org.springframework.context.ApplicationEvent, org.springframework.core.ResolvableType) 】我们可以看出监听器的执行是先判断多播器中是否存在 Executor ,如果存在,那么就单独开启一个线程执行,否则就同步执行,我们在初始化多播器的时候,可以为其设置一个 Executor ,那么就可以异步执行了。

    @Override
    	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);
    			}
    		}
    	}
    
  • 实现:在配置类注入一个多播器即可,bean的id一定要是 applicationEventMulticaster ,同时为其设置一个 executor

    /**
     * 自定义一个事件多播器,用来管理监听器和执行监听器
     * @return
     */
    @Bean(name = "applicationEventMulticaster")
    public ApplicationEventMulticaster applicationEventMulticaster(){
        //事件多播器
        SimpleApplicationEventMulticaster simpleApplicationEventMulticaster=new SimpleApplicationEventMulticaster();
        //设置executor
        SimpleAsyncTaskExecutor executor=new SimpleAsyncTaskExecutor();
        simpleApplicationEventMulticaster.setTaskExecutor(executor);
        //设置一个事件异常处理器,当监听器执行出现错误了会进行补救
        simpleApplicationEventMulticaster.setErrorHandler(t->{
            //这里可以针对不同的异常进行处理,在监听器中trycatch,不同执行抛出不同异常即可分类处理
            System.out.println("监听事件执行报错了");
            System.out.println(t.getMessage());
        });
        return simpleApplicationEventMulticaster;
    }
    

源码解析

  • 在spring源码中和事件涉及到的主要概念如下:
    • 事件(ApplicationEvent)
    • 监听器(ApplicationEventListener)
    • 事件发布器(ApplicationEventPublisher)
    • 事件多播器(ApplicationEventMulticaster)
  • 具体源码层面的涉及如下:
    • 容器刷新 refresh 方法中:
      • initApplicationEventMulticaster() :初始化事件多播器
      • registerListeners(); :注册事件监听器
      • finishRefresh() 方法中调用 publishEvent(new ContextRefreshedEvent(this)) 方法发布容器刷新事件。


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

硅谷之火

硅谷之火

保罗·弗赖伯格、迈克尔·斯韦因 / 张华伟 编译 / 中国华侨出版社 / 2014-11-1 / CNY 39.80

《硅谷之火:人与计算机的未来》以生动的故事,介绍了计算机爱好者以怎样的创新精神和不懈的努力,将计算机技术的力量包装在一个小巧玲珑的机壳里,实现了个人拥有计算机的梦想。同时以独特的视角讲述了苹果、微软、太阳微系统、网景、莲花以及甲骨文等公司的创业者们在实现个人计算机梦想的过程中创业的艰辛、守业的艰难、失败的痛苦,在激烈竞争的环境中奋斗的精神以及在技术上不断前进的历程。一起来看看 《硅谷之火》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

URL 编码/解码
URL 编码/解码

URL 编码/解码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试