原文链接:

1 Spring  监听事件 ApplicationListener 和 ApplicationEvent


2 在springboot中如何配置,及二次调用的解决



什么是ApplicationContext? 
它是Spring的核心,Context我们通常解释为上下文环境,但是理解成容器会更好些。 
ApplicationContext则是应用的容器。

Spring把Bean(object)放在容器中,需要用就通过get方法取出来。

 

ApplicationEvent

是个抽象类,里面只有一个构造函数和一个长整型的timestamp。

ApplicationListener

是一个接口,里面只有一个onApplicationEvent方法。

所以自己的类在实现该接口的时候,要实装该方法。


如果在上下文中部署一个实现了ApplicationListener接口的bean,

那么每当在一个ApplicationEvent发布到 ApplicationContext时,
这个bean得到通知。其实这就是标准的Oberver设计模式。

下面给出例子:

首先创建一个ApplicationEvent实现类:


[html]  view plain  copy


1. package com.spring.event;  
2.   
3. import org.springframework.context.ApplicationEvent;  
4.   
5. /**  
6. <p>Class:EmailListEvent</p>  
7. <p>Description:</p>  
8.  * @author Liam  
9.  * @Date [2012-9-7 上午8:42:07]  
10.  */  
11. public class EmailEvent extends ApplicationEvent {  
12.     /**  
13. <p>Description:</p>  
14.      */  
15. serialVersionUID = 1L;  
16.     public String address;    
17.     public String text;  
18.       
19.     public EmailEvent(Object source) {  
20.         super(source);  
21.     }  
22.       
23.     public EmailEvent(Object source, String address, String text) {  
24.         super(source);  
25. this.address = address;  
26. this.text = text;  
27.     }  
28.       
29.     public void print(){  
30.         System.out.println("hello spring event!");  
31.     }  
32.   
33. }

给出监听器:


[html]  view plain  copy



1. package com.spring.event;  
2.   
3. import org.springframework.context.ApplicationEvent;  
4. import org.springframework.context.ApplicationListener;  
5.   
6. /**  
7. <p>Class:EmailListener</p>  
8. <p>Description:</p>  
9.  * @author Liam  
10.  * @Date [2012-9-7 上午8:44:53]  
11.  */  
12. public class EmailListener implements ApplicationListener {  
13.   
14.     public void onApplicationEvent(ApplicationEvent  event) {  
15.         if(event instanceof EmailEvent){  
16. emailEvent = (EmailEvent)event;  
17.             emailEvent.print();  
18.             System.out.println("the source is:"+emailEvent.getSource());  
19.             System.out.println("the address is:"+emailEvent.address);  
20.             System.out.println("the email's context is:"+emailEvent.text);  
21.         }  
22.           
23.     }  
24.   
25. }

applicationContext.xml文件配置:

<bean id="emailListener" class="com.spring.event.EmailListener"></bean>

测试类:


[html]  view plain  copy



1. package com.spring.event;  
2.   
3. import org.springframework.context.ApplicationContext;  
4. import org.springframework.context.support.ClassPathXmlApplicationContext;  
5.   
6. /**  
7. <p>Class:Test</p>  
8. <p>Description:</p>  
9.  * @author Liam  
10.  * @Date [2012-9-6 上午10:41:34]  
11.  */  
12. public class Test {  
13.     public static void main(String[] args) {  
14. context = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");  
15.           
16. hello = (HelloBean) context.getBean("helloBean");  
17.         //hello.setApplicationContext(context);  
18. event = new EmailEvent("hello","boylmx@163.com","this is a email text!");  
19.         context.publishEvent(event);  
20.         //System.out.println();  
21.     }  
22. }

[html]  view plain  copy




  1. 测试结果:  


[html]  view plain  copy


1. <pre class="html" name="code">hello spring event!  
2. the source is:hello  
3. the address is:boylmx@163.com  
4. the email's context is:this is a email text!  
5. </pre>  
6. <pre></pre>  
7.


使用场景

在一些业务场景中,当容器初始化完成之后,需要处理一些操作,比如一些数据的加载、初始化缓存、特定任务的注册等等。这个时候我们就可以使用Spring提供的ApplicationListener来进行操作。

用法

本文以在Spring boot下的使用为例来进行说明。首先,需要实现ApplicationListener接口并实现onApplicationEvent方法。把需要处理的操作放在onApplicationEvent中进行处理:

package com.secbro.learn.context;

import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;

/**
 * Created by zhuzs on 2017/5/12.
 */
public class ApplicationStartListener implements ApplicationListener<ContextRefreshedEvent>{
    @Override
    public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
        System.out.println("我的父容器为:" + contextRefreshedEvent.getApplicationContext().getParent());
        System.out.println("初始化时我被调用了。");
    }
}

然后,实例化ApplicationStartListener这个类,在Spring boot中通过一个配置类来进行实例化:

package com.secbro.learn.conf;

import com.secbro.learn.context.ApplicationStartListener;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Created by zhuzs on 2017/5/12.
 */
@Configuration
public class ListenerConfig {

    @Bean
    public ApplicationStartListener applicationStartListener(){
        return new ApplicationStartListener();
    }
}

随后,启动Spring boot服务,打印出一下内容:

我的父容器为:null
初始化时我被调用了。
  • 1
  • 2

从打印的结果可以看出,ApplicationStartListener的onApplicationEvent方法在容器启动时已经被成功调用了。而此时初始化的容器为root容器。

二次调用问题

此处使用Spring boot来进行操作,没有出现二次调用的问题。在使用传统的application.xml和project-servlet.xml配置中会出现二次调用的问题。主要原因是初始化root容器之后,会初始化project-servlet.xml对应的子容器。我们需要的是只执行一遍即可。那么上面打印父容器的代码用来进行判断排除子容器即可。在业务处理之前添加如下判断:

if(contextRefreshedEvent.getApplicationContext().getParent() != null){
            return;
}

这样其他容器的初始化就会直接返回,而父容器(Parent为null的容器)启动时将会执行相应的业务操作。

关联知识

在spring中InitializingBean接口也提供了类似的功能,只不过它进行操作的时机是在所有bean都被实例化之后才进行调用。根据不同的业务场景和需求,可选择不同的方案来实现。