zoukankan      html  css  js  c++  java
  • Spring ApplicationListener 理解

    在开发时有时候需要在整个应用开始运行时执行一些特定代码,比如初始化环境,准备测试数据、加载一些数据到内存等等。

    spring中可以通过ApplicationListener来实现相关的功能,加载完成后触发contextrefreshedevent事件(上下文件刷新事件)。

    ApplicationContext事件机制是观察者设计模式的实现,通过ApplicationEvent类和ApplicationListener接口,可以实现ApplicationContext事件处理。

    如果容器中有一个ApplicationListener Bean,每当ApplicationContext发布ApplicationEvent时,ApplicationListener Bean将自动被触发。这种事件机制都必须需要程序显示的触发。

    其中spring有一些内置的事件,当完成某种操作时会发出某些事件动作。比如监听ContextRefreshedEvent事件,当所有的bean都初始化完成并被成功装载后会触发该事件,实现ApplicationListener<ContextRefreshedEvent>接口可以收到监听动作,然后可以写自己的逻辑。

    同样事件可以自定义、监听也可以自定义,完全根据自己的业务逻辑来处理。

    内置事件

    序号         Spring 内置事件 & 描述

    1          ContextRefreshedEvent

              ApplicationContext 被初始化或刷新时,该事件被发布。这也可以在 ConfigurableApplicationContext接口中使用 refresh() 方法来发生。此处的初始化是指:所有的Bean被成功装载,后处理Bean被检测并激活,所有Singleton Bean 被预实例化,ApplicationContext容器已就绪可用。

    2          ContextStartedEvent

              当使用 ConfigurableApplicationContext (ApplicationContext子接口)接口中的 start() 方法启动 ApplicationContext 时,该事件被发布。你可以调查你的数据库,或者你可以在接受到这个事件后重启任何停止的应用程序。

    3          ContextStoppedEvent

              当使用 ConfigurableApplicationContext 接口中的 stop() 停止 ApplicationContext 时,发布这个事件。你可以在接受到这个事件后做必要的清理的工作。


    4          ContextClosedEvent

              当使用 ConfigurableApplicationContext 接口中的 close() 方法关闭 ApplicationContext 时,该事件被发布。一个已关闭的上下文到达生命周期末端;它不能被刷新或重启。

    5          RequestHandledEvent

              这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务。只能应用于使用DispatcherServlet的Web应用。在使用Spring作为前端的MVC控制器时,当Spring处理用户请求结束后,系统会自动触发该事件。

    业务方监听事件举例

    比如要监听ContextRefreshedEvent的时可以实现ApplicationListener接口,并且传入要监听的事件

    1 @Component
    2 public class TestApplicationListener implements ApplicationListener<ContextRefreshedEvent>{
    3     @Override
    4     public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
    5         System.out.println(contextRefreshedEvent);
    6         System.out.println("TestApplicationListener............................");
    7     }
    8 }

      

    自定义事件

    可以自定义事件,然后做完业务处理后手动发出。同上集成某个监听接口,接收到事件后进行业务处理

    事件定义:

     1 public class EmailEvent extends ApplicationEvent{
     2    private String address;
     3    private String text;
     4    public EmailEvent(Object source, String address, String text){
     5    super(source);
     6       this.address = address;
     7       this.text = text;
     8    }
     9    public EmailEvent(Object source) {
    10      super(source);
    11    }
    12    //......address和text的setter、getter
    13 }

    监听定义

     1 public class EmailNotifier implements ApplicationListener{
     2    public void onApplicationEvent(ApplicationEvent event) {
     3      if (event instanceof EmailEvent) {
     4         EmailEvent emailEvent = (EmailEvent)event;
     5         System.out.println("邮件地址:" + emailEvent.getAddress());
     6         System.our.println("邮件内容:" + emailEvent.getText());
     7      } else {
     8         System.our.println("容器本身事件:" + event);
     9      }
    10    }
    11 }

    业务触发

    1 public class SpringTest {
    2    public static void main(String args[]){
    3      ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
    4      //创建一个ApplicationEvent对象
    5      EmailEvent event = new EmailEvent("hello","abc@163.com","This is a test");
    6      //主动触发该事件
    7      context.publishEvent(event);
    8    }
    9 }

    不管是内置监听还是外部自定义监听一定要把实现ApplicationListener的类定义成一个bean才行,可以是通过注解@Component等也可以通过xml的方式去执行。

    但是这个时候,会存在一个问题,在web 项目中(spring mvc),系统会存在两个容器,一个是root application context ,另一个就是我们自己的 projectName-servlet  context(作为root application context的子容器)。

    这种情况下,就会造成onApplicationEvent方法被执行两次。为了避免上面提到的问题,我们可以只在root application context初始化完成后调用逻辑代码,其他的容器的初始化完成,则不做任何处理,修改后代码。event.getApplicationContext().getDisplayName().equals("Root WebApplicationContext") 。

    如下:(要把实现ApplicationListener的类定义成一个bean)

     1 @Component
     2 public class SpringStartedListener implements ApplicationListener<ContextRefreshedEvent> {
     3 
     4     private static final Logger logger = LoggerFactory.getLogger(SpringStartedListener.class);
     5     private static final String CONTEXT_DISPLAY_NAME = "WebApplicationContext";
     6 
     7     @Autowired
     8     private ThreadPoolTaskExecutor threadPoolTaskExecutor;
     9 
    10     @Override
    11     public void onApplicationEvent(ContextRefreshedEvent event) {
    12 
    13         if (event.getApplicationContext().getDisplayName().indexOf(CONTEXT_DISPLAY_NAME) > -1) {
    14             ConfigConstants.initConfig(); // 初始化配置
    15             threadPoolTaskExecutor.submit(() -> {
    16                 //在这里启动netty
    17                 Server server = NettyServer.getInstance();
    18                 try {
    19                     logger.debug("NettyServer Starting...");
    20                     server.start();
    21                 } catch (InterruptedException e) {
    22                     logger.error("init NettyServer err:{}", e.getMessage(), e);
    23                 }
    24             });
    25         }
    26     }
    27 }
  • 相关阅读:
    递归---Day29
    File类---Day28
    线程池---Day26
    微信公众号运营人员必备的软件工具网站合集
    消息中间件的技术选型心得-RabbitMQ、ActiveMQ和ZeroMQ
    itoa函数的递归实现(二级指针实现)
    二叉树——查找两个任意节点的最近祖先
    C#多线程(二)
    C#多线程(一)
    Websocket协议之php实现
  • 原文地址:https://www.cnblogs.com/panchanggui/p/10238919.html
Copyright © 2011-2022 走看看