• spring事件通知机制详解


    优势

    • 解耦
    • 对同一种事件有多种处理方式
    • 不干扰主线(main line)

    起源

    要讲spring的事件通知机制,就要先了解一下spring中的这些接口和抽象类:

    • ApplicationEventPublisherAware        接口:用来 publish event

    • ApplicationEvent                  抽象类,记录了source和初始化时间戳:用来定义Event

    • ApplicationListener<E extends ApplicationEvent>  :用来监听事件

    构建自己的事件机制案例

    测试案例

    测试入口

     1 package com.meituan.spring.testcase.listener;
     2 
     3 import org.springframework.context.support.ClassPathXmlApplicationContext;
     4 
     5 import java.util.concurrent.TimeUnit;
     6 
     7 /**
     8  * Created by zhangxiaoguang on 16/1/27 下午11:40.
     9  * -----------------------------
    10  * Desc:
    11  */
    12 public class TestPortal {
    13    public static void main(String[] args) throws InterruptedException {
    14 
    15       final ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-config.xml");
    16 
    17       String[] definitionNames = applicationContext.getBeanDefinitionNames();
    18       System.out.println("==============bean====start=================");
    19       for (String definitionName : definitionNames) {
    20          System.out.println("bean----:" + definitionName);
    21       }
    22       System.out.println("==============bean====end=================");
    23       System.out.println();
    24       final CustomizePublisher customizePublisher = applicationContext.getBean(CustomizePublisher.class);
    25 
    26 
    27       Thread thread = new Thread(new Runnable() {
    28          @Override
    29          public void run() {
    30             try {
    31                System.out.println("开始吃饭:");
    32 
    33                MealEvent lunchEvent = new MealEvent("A吃午饭了", MealEnum.lunch);
    34                MealEvent breakfastEvent = new MealEvent("B吃早饭了", MealEnum.breakfast);
    35                MealEvent dinnerEvent = new MealEvent("C吃晚饭了", MealEnum.dinner);
    36                customizePublisher.publish(lunchEvent);
    37                TimeUnit.SECONDS.sleep(1l);
    38                customizePublisher.publish(breakfastEvent);
    39                TimeUnit.SECONDS.sleep(1l);
    40                customizePublisher.publish(dinnerEvent);
    41                TimeUnit.SECONDS.sleep(1l);
    42 
    43                System.out.println("他们吃完了!");
    44             } catch (InterruptedException e) {
    45                e.printStackTrace();
    46             }
    47          }
    48       });
    49       thread.setName("meal-thread");
    50       thread.start();
    51 
    52       System.out.println(Thread.currentThread().getName() + " is waiting for ....");
    53       thread.join();
    54       System.out.println("Done!!!!!!!!!!!!");
    55    }
    56 }
    TestPortal

    测试结果

    测试成员

    • MealListener :MealEvent                  演员

    • TroubleListener :TroubleEvent         演员

    • AllAcceptedListener                            演员

    • MealEnum                                          道具

    • TestPortal                                           入口

    • CustomizePublisher                           导演

    成员代码

    接受全部事件的演员(很负责任啊)

     1 package com.meituan.spring.testcase.listener;
     2 
     3 import org.springframework.context.ApplicationEvent;
     4 import org.springframework.context.ApplicationListener;
     5 import org.springframework.stereotype.Component;
     6 
     7 /**
     8  * Created by zhangxiaoguang on 16/1/27 下午11:27.
     9  * -----------------------------
    10  * Desc:
    11  */
    12 @Component
    13 public class AllAcceptedListener implements ApplicationListener<ApplicationEvent> {
    14    @Override
    15    public void onApplicationEvent(ApplicationEvent event) {
    16       System.out.println(">>>>>>>>>>>>>>>>event:" + event);
    17    }
    18 }
    AllAcceptedListener

    导演负责分发事件

     1 package com.meituan.spring.testcase.listener;
     2 
     3 import org.springframework.context.ApplicationEventPublisher;
     4 import org.springframework.context.ApplicationEventPublisherAware;
     5 import org.springframework.stereotype.Component;
     6 
     7 /**
     8  * Created by zhangxiaoguang on 16/1/28 上午1:41.
     9  * -----------------------------
    10  * Desc:
    11  */
    12 @Component
    13 public class CustomizePublisher implements ApplicationEventPublisherAware {
    14 
    15    private ApplicationEventPublisher applicationEventPublisher;
    16 
    17    public void publish(MealEvent event) {
    18       applicationEventPublisher.publishEvent(event);
    19    }
    20 
    21    @Override
    22    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
    23       this.applicationEventPublisher = applicationEventPublisher;
    24    }
    25 }
    CustomizePublisher

    负责处理吃饭事件的演员

     1 package com.meituan.spring.testcase.listener;
     2 
     3 import org.springframework.context.ApplicationListener;
     4 import org.springframework.stereotype.Component;
     5 
     6 /**
     7  * Created by zhangxiaoguang on 16/1/27 下午11:27.
     8  * -----------------------------
     9  * Desc:
    10  */
    11 @Component
    12 public class MealListener implements ApplicationListener<MealEvent> {
    13    @Override
    14    public void onApplicationEvent(MealEvent event) {
    15       System.out.println(String.format(">>>>>>>>>>>thread:%s,type:%s,event:%s",
    16             Thread.currentThread().getName(), event.getMealEnum(), event));
    17 
    18       dispatchEvent(event);
    19    }
    20 
    21    private void dispatchEvent(MealEvent event) {
    22       switch (event.getMealEnum()) {
    23          case breakfast:
    24             System.out.println(event.getMealEnum() + " to handle!!!");
    25             break;
    26          case lunch:
    27             System.out.println(event.getMealEnum() + " to handle!!!");
    28             break;
    29          case dinner:
    30             System.out.println(event.getMealEnum() + " to handle!!!");
    31             break;
    32          default:
    33             System.out.println(event.getMealEnum() + " error!!!");
    34             break;
    35       }
    36    }
    37 }
    MealListener

    吃饭消息

     1 package com.meituan.spring.testcase.listener;
     2 
     3 import org.springframework.context.ApplicationEvent;
     4 
     5 /**
     6  * Created by zhangxiaoguang on 16/1/27 下午11:24.
     7  * -----------------------------
     8  * Desc:吃饭事件
     9  */
    10 public class MealEvent extends ApplicationEvent {
    11 
    12    private MealEnum mealEnum;
    13 
    14    /**
    15     * @param mealContent
    16     *        吃什么
    17     * @param mealEnum
    18     *        早餐还是午餐?
    19     */
    20    public MealEvent(String mealContent, MealEnum mealEnum) {
    21       super(mealContent);
    22       this.mealEnum = mealEnum;
    23    }
    24 
    25    public MealEnum getMealEnum() {
    26       return mealEnum;
    27    }
    28 }
    MealEvent

    工具

     1 package com.meituan.spring.testcase.listener;
     2 
     3 /**
     4  * Created by zhangxiaoguang on 16/1/27 下午11:29.
     5  * -----------------------------
     6  * Desc:
     7  */
     8 public enum MealEnum {
     9    breakfast,
    10    lunch,
    11    dinner
    12 }
    MealEnum

    令人厌烦的演员

     1 package com.meituan.spring.testcase.listener;
     2 
     3 import org.springframework.context.ApplicationListener;
     4 import org.springframework.stereotype.Component;
     5 
     6 /**
     7  * Created by zhangxiaoguang on 16/1/27 下午11:27.
     8  * -----------------------------
     9  * Desc:
    10  */
    11 @Component
    12 public class TroubleListener implements ApplicationListener<TroubleEvent> {
    13    @Override
    14    public void onApplicationEvent(TroubleEvent event) {
    15       System.out.println(">>>>>>>>>>>>>>>>event:" + event);
    16    }
    17 }
    TroubleListener

    令人厌烦的事件

     1 package com.meituan.spring.testcase.listener;
     2 
     3 import org.springframework.context.ApplicationEvent;
     4 
     5 /**
     6  * Created by zhangxiaoguang on 16/1/27 下午11:24.
     7  * -----------------------------
     8  * Desc:令人厌烦的事件
     9  */
    10 public class TroubleEvent extends ApplicationEvent {
    11    public TroubleEvent(Object source) {
    12       super(source);
    13    }
    14 }
    TroubleEvent

    总结

    详细定制 event 类型的,则相关定制的listener会处理对应的消息,其他listener不会管闲事;

    制定顶级 event 类型的,ApplicationEvent的,则会处理所有的事件。

    ApplicationEvent

    依赖关系

    ContextEvent事件机制简介

    ContextRefreshedEvent:当整个ApplicationContext容器初始化完毕或者刷新时触发该事件;

     1 @Override
     2 public void refresh() throws BeansException, IllegalStateException {
     3    synchronized (this.startupShutdownMonitor) {
     4       ......
     5 
     6       try {
     7          
     8          ......
     9 
    10          // Last step: publish corresponding event.
    11          finishRefresh();
    12       }
    13 
    14       catch (BeansException ex) {
    15          ......
    16       }
    17    }
    18 }
    19 protected void finishRefresh() {
    20    // Initialize lifecycle processor for this context.
    21    initLifecycleProcessor();
    22 
    23    // Propagate refresh to lifecycle processor first.
    24    getLifecycleProcessor().onRefresh();
    25 
    26    // Publish the final event.
    27    publishEvent(new ContextRefreshedEvent(this));
    28 
    29    // Participate in LiveBeansView MBean, if active.
    30    LiveBeansView.registerApplicationContext(this);
    31 }
    View Code

    ContextClosedEvent:当ApplicationContext doClose时触发该事件,这个时候会销毁所有的单例bean; 

     1 @Override
     2 public void registerShutdownHook() {
     3    if (this.shutdownHook == null) {
     4       // No shutdown hook registered yet.
     5       this.shutdownHook = new Thread() {
     6          @Override
     7          public void run() {
     8             doClose();
     9          }
    10       };
    11       Runtime.getRuntime().addShutdownHook(this.shutdownHook);
    12    }
    13 }
    14 @Override
    15 public void close() {
    16    synchronized (this.startupShutdownMonitor) {
    17       doClose();
    18       // If we registered a JVM shutdown hook, we don't need it anymore now:
    19       // We've already explicitly closed the context.
    20       if (this.shutdownHook != null) {
    21          try {
    22             Runtime.getRuntime().removeShutdownHook(this.shutdownHook);
    23          }
    24          catch (IllegalStateException ex) {
    25             // ignore - VM is already shutting down
    26          }
    27       }
    28    }
    29 }
    30 protected void doClose() {
    31    if (this.active.get() && this.closed.compareAndSet(false, true)) {
    32       ......
    33  
    34       try {
    35          // Publish shutdown event.
    36          publishEvent(new ContextClosedEvent(this));
    37       }
    38       catch (Throwable ex) {
    39          logger.warn("Exception thrown from ApplicationListener handling ContextClosedEvent", ex);
    40       }
    41  
    42       ......
    43    }
    44 }
    View Code

    ContextStartedEvent:当ApplicationContext start时触发该事件; 

    1 @Override
    2 public void start() {
    3    getLifecycleProcessor().start();
    4    publishEvent(new ContextStartedEvent(this));
    5 }

    ContextStoppedEvent:当ApplicationContext stop时触发该事件; 

    1 @Override
    2 public void stop() {
    3    getLifecycleProcessor().stop();
    4    publishEvent(new ContextStoppedEvent(this));
    5 }

    ApplicationListener 

    依赖关系

    带你一步步走向源码的世界

    从上边打印的线程信息可以知道,spring处理事件通知采用的是当前线程,并没有为为我们启动新的线程,所以,如果需要,你要自己处理线程信息哦,当然也可以设定(如何设置?)!

    AbstractApplicationContext

    补齐:同一个event,被多个listener监听,先被哪个listener执行是由下边的代码决定的:

    如何设置线程池?

    回到上边的问题,到底该如何设置线程池呢?

    AbstractApplicationEventMulticaster 是private的,并且没有提供写入方法...

    实际案例

    用在自己的代码里就是最好的例子了 ^_^

  • 相关阅读:
    ssm(Spring+Spring mvc+mybatis)Service层实现类——DeptServiceImpl
    JBoss7 安装配置
    JBoss7 安装配置
    在Eclipse/MyEclipse中安装spket插件
    [转载]Node.js是什么
    [转载]Node.js是什么
    iis调度tomcat
    iis调度tomcat
    [转载]Win7 系统安装 配置IIS 详细图解
    在window环境下,设置tomcat自启动服务
  • 原文地址:https://www.cnblogs.com/zhangxiaoguang/p/spring-notification.html
Copyright © 2020-2023  润新知