前言
在使用spring的过程中,一般很少直接使用BeanFactory示例作为容器,更多的是使用ApplicationContext示例作为容器。
ApplicationContext容器提供观察者模式的时间机制
- ApplicationEvent:事件
- ApplicationListener:时间监听
- ApplicationContext:容器
 应用程序只需要创建一个event并发布到容器中,Listener就会监听到此事件并进行处理。
 此篇文章我们就来详细讨论一下Spring事件的各种用法。
自定义事件
借助spring提供的事件模型,我们可以很方便的定义、发布和监听自定义事件。
事件对象
自定义的事件对象需要实现ApplicationEvent接口(spring4.2版本后可以不实现,将在下面介绍)
比如我们定义一个MyEvent事件对象,其中text属性模拟事件属性1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24/**
 * 自定义事件
 */
public class MyEvent extends ApplicationEvent {
  private String text; //事件内容
  public MyEvent(Object source) {
    super(source);
  }
  public MyEvent(Object source, String text) {
    super(source);
    this.text = text;
  }
  public String getText() {
    return text;
  }
  public void setText(String text) {
    this.text = text;
  }
}
发布事件
我们只要通过ApplicationContext的publishEvent方法就可以发布一个自定义消息。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17/**
 * 容器
 */
public class Application {
  public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(Application.class);
    //新建事件
    MyEvent myEvent = new MyEvent("myEvent", "我是自定义事件的内容");
    //向容器发布时间
    ctx.publishEvent(myEvent);
  }
}
事件监听
我们可以通过实现ApplicationListener接口来创建监听1
2
3
4
5
6
7
8
9
10
11/**
 * 自定义事件监听
 */
public class MyEventListener implements ApplicationListener<MyEvent> {
  
  public void onApplicationEvent(MyEvent event) {
    System.out.println("监听到自定义事件:" + event.getText());
  }
}
当然也可以使用更古老的方式监听,这种写法逻辑不清晰,个人不推荐1
2
3
4
5
6
7
8
9
10
11
12
13/**
 * 事件监听
 */
public class MyEventListener2 implements ApplicationListener {
  
  public void onApplicationEvent(ApplicationEvent event) {
    if (event instanceof MyEvent) {
      MyEvent myEvent = (MyEvent) event;
      System.out.println("监听到自定义事件:" + myEvent.getText());
    }
  }
}
执行结果
运行Application的main函数后控制台输出1
监听到自定义事件:我是自定义事件的内容
spring内置的事件
Spring内置的ApplicationEvent有
| 事件 | 说明 | 
|---|---|
| ContextClosedEvent | 当ApplicationContext被关闭时触发该事件。容器被关闭时,其管理的所有单例Bean都被销毁。 | 
| ContextRefreshedEvent | 当ApplicationContext初始化或者刷新时触发该事件 | 
| ContextStartedEvent | 容器启动时发布 | 
| ContextStoppedEvent | 当容器调用ConfigurableApplicationContext的Stop()方法停止容器时触发该事件 | 
| PayloadApplicationEvent | 在spring4.2中新引入的事件,大部分由spring内部使用;spring4.2之后向容器中发布的事件,不再强制要求继承自ApplicationEvent,当发布一个非ApplicationEvent对象时,spring会自动包装成PayloadApplicationEvent。 | 
| RequestHandledEvent | 这是一个 web-specific 事件,告诉所有 bean HTTP 请求已经被服务 | 
事件监听
ContextRefreshedEvent事件监听器
| 1 | /** | 
ContextStartedEvent事件监听器
| 1 | /** | 
ContextClosedEvent事件监听器
| 1 | /** | 
ContextStoppedEvent事件监听器
| 1 | /** | 
发布事件
编写容器,其中ApplicationContext更强制设置为AnnotationConfigApplicationContext类型。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19/**
 * 容器
 */
public class Application {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(Application.class);
    ctx.start();
    MyEvent myEvent = new MyEvent("myEvent", "我是自定义事件的内容");  //新建事件
    ctx.publishEvent(myEvent); //向容器发布时间
    ctx.stop();
    ctx.close();
  }
}
执行结果
| 1 | 监听到ContextRefreshedEvent事件 | 
PayloadApplicationEvent用法
Spring4.2版本优化了自定义事件类的写法,不再强制要求实现ApplicationEvent接口,定义一个普通的java类,然后通过容器发布。容器会自动将此对象包装成PayloadApplicationEvent事件,然后通过监听此事件获取原始对象。
事件对象
编写一个普通的java对象作为事件对象1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18/**
 * 未实现ApplicationEvent的事件对象
 */
public class NormalEvent {
  private String text;
  public NormalEvent(String text) {
    this.text = text;
  }
  public String getText() {
    return text;
  }
  public void setText(String text) {
    this.text = text;
  }
}
事件监听器
注意,此处监听了PayloadApplicationEvent事件,然后从此事件中获取被包装的原始对象1
2
3
4
5
6
7
8
9
10
11
12
13/**
 * PayloadApplicationEvent事件监听
 */
public class NormalEventListener implements ApplicationListener<PayloadApplicationEvent> {
  
  public void onApplicationEvent(PayloadApplicationEvent event) {
    if (event.getPayload() instanceof NormalEvent) {
      NormalEvent normalEvent = (NormalEvent) event.getPayload();
      System.out.println("监听到自定义事件:" + normalEvent.getText());
    }
  }
}
@EventListener用法
Spring4.2还增加了@EventListener注解。
通过此注解监听器不再需要实现ApplicationListener接口,给普通的方法增加@EventListener注解,spring会自动注册一个ApplicationListener来匹配方法签名。
发布事件
可以看到,我们发布的是一个未实现ApplicationEvent的普通java对象1
2
3
4
5
6
7
8
9
10
11
12
13/**
 * 容器
 */
public class Application {
  public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(Application.class);
   
    ctx.publishEvent(new NormalEvent("我是一个事件,但是并未实现ApplicationEvent接口")); 
  }
}
事件监听
| 1 | /** | 
执行结果
| 1 | 监听到自定义事件:我是一个事件,但是并未实现ApplicationEvent接口 | 
从上面可以看出来,从Spring4.2后,事件不再需要实现ApplicationEvent接口,监听器也不再需要实现ApplicationListener接口,通过简单自动包装事件对象和@EventListener注解,大大简化了事件的实现。
带条件的监听器
@EventListener注解,可以通过classes属性指定监听的消息类型(可以多个),还可以通过condition属性增加监听条件(SpELg表达式),例如下面1
2
3
4
5
6
7
8
9
10
11/**
 * 使用EventListener注解的带条件的监听器
 */
public class NormalEventListener3 {
  (classes = {NormalEvent.class}, condition = "#normalEvent.text==\"test\"")
  public void handelNormalEvent(NormalEvent normalEvent) {
    System.out.println("监听到自定义事件:" + normalEvent.getText());
  }
}
| 1 | /** | 
执行结果1
监听到自定义事件:test
从结果可以看出,只有第二个发布的事件被监听到了,通过这个功能,我们可以方便的监听符合条件的事件。
@EventListener注解方法返回非null
对于任何一个使用@EventListener注解的方法,允许定一个非void的返回类型。如果返回一个非null值,Spring将此结果作为一个新事件发布到容器。
容器
| 1 | /** | 
监听器
| 1 | /** | 
执行结果
| 1 | NormalEventListener4监听到自定义事件:我是一个事件,但是并未实现ApplicationEvent接口 | 
@TransactionalEventListener注解
使用此注解,可以指定只有当外部事务完成后,再执行监听器。
注意点
由于Spring的事件处理是单线程的,如果一个事件被发布,在所有监听者处理完成前,该进程将被阻塞。
因此对于耗时的监听处理逻辑需要小心处理。如果碰到这样耗时的处理,可以考虑使用@Anyc注解来启动单独线程进行处理;对于@Anyc注解的用法,将来单独开文章解释。
参考文章:
https://projects.spring.io/spring-framework/
http://blog.csdn.net/xiejx618/article/details/44600369
http://blog.csdn.net/chenssy/article/details/8220089