1.基于注解驱动事件监听器:现在可以在一个Bean的方法上使用@EventListener注解来自动注册一个ApplicationListener来匹配方法签名.

@Component
public class MyListener {
@EventListener
public void handleContextRefresh(ContextRefreshedEvent event) { }
}

此方法签名定义了你感兴趣的事件类型.也可以定义SpELg表达式来匹配处理这个事件.假设事件的定义如下:

public class OrderCreatedEvent implements CreationEvent<Order> {
private boolean awesome;
public boolean isAwesome() {
return this.awesome;
} }

下面的例子事件监听器将同时满足以下情况才会被调用:a.它是CreationEvent<Order>类型的事件;b.此事件的awesome标志为true.

@Component
public class MyComponent {
@EventListener(condition = "#creationEvent.awesome")
public void handleOrderCreatedEvent(CreationEvent<Order> creationEvent) { } }

2.上面只提到了注册事件和事件定义,再来看看事件的发布.
对于任何一个使用@EventListener注解的方法,你可以定义一个非void返回类型.如果你返回一个非null值作为处理一个常规事件的结果,我们会将此结果作为一个新事件来发送.

你可能注意到OrderCreatedEvent并没有继承ApplicationEvent,我们觉得是时候让你灵活发布任意事件,而不强迫你去继承ApplicationEvent.ApplicationEventPublisher已被扩展来允许你发布任意对象.当这个对象不是一个ApplicationEvent,我们会使用PayloadApplicationEvent来为你包装.以下例子演示了你如何使用ApplicationEventPublisher来发送一个OrderCreatedEvent:

@Component
public class MyComponent {
private final ApplicationEventPublisher publisher;
@Autowired
public MyComponent(ApplicationEventPublisher publisher) {
this.publisher=publisher;
}
public void createOrder(Order order) {
this.publisher.publishEvent(new OrderCreatedEvent(order));
} }

3.事务边界事件
另一个受欢迎的改善是一个事件的监听器绑定到该事务一个阶段的能力。典型的例子是当事务成功完成时,再处理这个事件.下面以这样的方式重写上面的例子,当生产者运行的事务已成功完成时,此订单创建事件才会被处理.

@Component
public class MyComponent {
@TransactionalEventListener(condition = "#creationEvent.awesome")
public void handleOrderCreatedEvent(CreationEvent<Order> creationEvent) { } }

功能的实现大致:

a.注册Listener:实例化spring容器的时候会注册一个org.springframework.context.event.EventListenerMethodProcessor这样的Bean,完成初始化,会调用它的兵后置回调afterSingletonsInstantiated()方法:

@Override
public void afterSingletonsInstantiated() {
List<EventListenerFactory> factories = getEventListenerFactories();
String[] allBeanNames = this.applicationContext.getBeanNamesForType(Object.class);
for (String beanName : allBeanNames) {
if (!ScopedProxyUtils.isScopedTarget(beanName)) {
Class<?> type = this.applicationContext.getType(beanName);
try {
processBean(factories, beanName, type);
}catch (RuntimeException e) {
throw new BeanInitializationException("Failed to process @EventListener " +
"annotation on bean with name '" + beanName + "'", e);
}
}
}
}

迭代每个Bean进行processBean(factories, beanName, type)处理.

protected void processBean(List<EventListenerFactory> factories, String beanName, final Class<?> type) {
Class<?> targetType = getTargetClass(beanName, type);
if (!this.nonAnnotatedClasses.contains(targetType)) {
final Set<Method> annotatedMethods = new LinkedHashSet<Method>(1);
Method[] methods = ReflectionUtils.getUniqueDeclaredMethods(targetType);
for (Method method : methods) {
//在这个方法上查找EventListener注解
EventListener eventListener = AnnotationUtils.findAnnotation(method, EventListener.class);
if (eventListener == null) {
continue;
}
for (EventListenerFactory factory : factories) {
if (factory.supportsMethod(method)) {
if (!type.equals(targetType)) {
method = getProxyMethod(type, method);
}
//使用这个方法创建一个ApplicationListener对象
ApplicationListener<?> applicationListener =
factory.createApplicationListener(beanName, type, method);
if (applicationListener instanceof ApplicationListenerMethodAdapter) {
((ApplicationListenerMethodAdapter)applicationListener)
.init(this.applicationContext, this.evaluator);
}
//添加到applicationContext的事件广播器
this.applicationContext.addApplicationListener(applicationListener);
annotatedMethods.add(method);
break;
}
}
}
if (annotatedMethods.isEmpty()) {
this.nonAnnotatedClasses.add(type);
if (logger.isTraceEnabled()) {
logger.trace("No @EventListener annotations found on bean class: " + type);
}
}
else {
// Non-empty set of methods
if (logger.isDebugEnabled()) {
logger.debug(annotatedMethods.size() + " @EventListener methods processed on bean '" + beanName +
"': " + annotatedMethods);
}
}
}
}

b.通过ApplicationEventPublisher发布事件:入口在org.springframework.context.support.AbstractApplicationContext#publishEvent(java.lang.Object),再调用重载方法

protected void publishEvent(Object event, ResolvableType eventType) {
Assert.notNull(event, "Event must not be null");
if (logger.isTraceEnabled()) {
logger.trace("Publishing event in " + getDisplayName() + ": " + event);
}
final ApplicationEvent applicationEvent;
//这里可以看出如果event不是ApplicationEvent类型,就会使用PayloadApplicationEvent进行包装
if (event instanceof ApplicationEvent) {
applicationEvent = (ApplicationEvent) event;
}
else {
applicationEvent = new PayloadApplicationEvent<Object>(this, event);
if (eventType == null) {
eventType = ResolvableType.forClassWithGenerics(PayloadApplicationEvent.class, event.getClass());
}
}
//通过事件广播器进行广播事件
getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);
if (this.parent != null) {
if (this.parent instanceof AbstractApplicationContext) {
((AbstractApplicationContext) this.parent).publishEvent(event, eventType);
}
else {
this.parent.publishEvent(event);
}
}
}

spring4.2更好的应用事件的更多相关文章

  1. javascript中的事件冒泡和事件捕获

    1.事件冒泡 IE 的事件流叫做事件冒泡(event bubbling),即事件开始时由最具体的元素(文档中嵌套层次最深的那个节点)接收,然后逐级向上传播到较为不具体的节点(文档).以下面的HTML ...

  2. javaScirpt事件详解-原生事件基础(一)

    事件 JavaScript与HTML之间的交互是通过事件实现的.事件,就是文档或浏览器窗口中发生的一些特定的交互瞬间,通过监听特定事件的发生,你能响应相关的操作.图片引用:UI Events 事件流 ...

  3. DOM事件

    在慕课网上学习了DOM事件探秘课程,特此整理了一下笔记. 慕课网DOM事件探秘课程地址:http://www.imooc.com/learn/138 事件 是文档或浏览器窗口中发生的特定的交互瞬间.[ ...

  4. jQuery 2.0.3 源码分析 事件体系结构

    那么jQuery事件处理机制能帮我们处理那些问题? 毋容置疑首先要解决浏览器事件兼容问题 可以在一个事件类型上添加多个事件处理函数,可以一次添加多个事件类型的事件处理函数 提供了常用事件的便捷方法 支 ...

  5. 解密jQuery事件核心 - 模拟事件(四)

    前几章已经把最核心的实现都分解过了,这一章我们看看jQuery是如何实现事件模拟的 在Internet Explorer 8和更低,一些事件change 和 submit本身不冒泡,但jQuery修改 ...

  6. 理解DOM事件流的三个阶段

    本文主要解决两个问题: 1.什么是事件流 2.DOM事件流的三个阶段 事件流之事件冒泡与事件捕获 在浏览器发展的过程中,开发团队遇到了一个问题.那就是页面中的哪一部分拥有特定的事件? 可以想象画在一张 ...

  7. 深入理解DOM事件机制系列第一篇——事件流

    × 目录 [1]历史 [2]事件冒泡 [3]事件捕获[4]事件流 前面的话 javascript操作CSS称为脚本化CSS,而javascript与HTML的交互是通过事件实现的.事件就是文档或浏览器 ...

  8. 你真的了解DOM事件么?

    你真的了解DOM事件么? 我们大家都知道,人与人之间的交流可以通过语言,文字,肢体动作,面部微表情等,但是你知道Javascript和HTML之间是通过什么进行交互的么?你又知道Javascript和 ...

  9. DOM事件揭秘-事件流

    事件:文档/窗口中发生的特定的交互瞬间 瀑布流,图片轮播 动作都是通过事件触发的 课程内容: 1,理解事件流 2,使用时间处理程序 3,不同的事件类型 ie4.0以后, 事件流:描述的是从页面中接收事 ...

随机推荐

  1. hdu 1565&hdu 1569(网络流--最小点权值覆盖)

    方格取数(1) Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Su ...

  2. LeetCode OJ--Construct Binary Tree from Preorder and Inorder Traversal *

    http://oj.leetcode.com/problems/construct-binary-tree-from-preorder-and-inorder-traversal/ 根据二叉树的前序遍 ...

  3. 关于Red5整合springMVC提示scope not found 的错误

    https://www.cnblogs.com/qgc88:

  4. WEB接口测试之Jmeter接口测试自动化 (三)(数据驱动测试)

     接口测试与数据驱动 1简介 数据驱动测试,即是分离测试逻辑与测试数据,通过如excel表格的形式来保存测试数据,用测试脚本读取并执行测试的过程. 2 数据驱动与jmeter接口测试 我们已经简单介绍 ...

  5. java 判断请求来自手机端还是电脑端

    根据当前请求的特征,判断该请求是否来自手机终端,主要检测特殊的头信息,以及user-Agent这个header public static boolean isMobileDevice(HttpSer ...

  6. k8s之Service资源

    1.Service概述 service是kubernetes中最核心的资源对象之一,service和pod之间是通过Label串起来,相同的Service的pod的Label是一样的.同一个servi ...

  7. 笔记-迎难而上之Java基础进阶6

    import java.io.*; public class InputStreamDemo{ public static void main(String[] args) throws IOExce ...

  8. JVM--------3

    一.为什么要使用类加载器?Java语言里,类加载都是在程序运行期间完成的,这种策略虽然会令类加载时稍微增加一些性能开销,但是会给java应用程序提供高度的灵活性.例如:1.编写一个面向接口的应用程序, ...

  9. java计算年龄

    精确到天计算年龄 public static int getAgeByCardId(String card) throws Exception { Integer len = card.length( ...

  10. 简单过下bash/sh脚本基本知识吧

    1.sh/bash要注意空格的使用 2. for循环: for i in /etc/profile.d/*.sh ; do if [ -r "$i" ]; then if [ &q ...