spring 事件

目前的版本是spring 4.3.10 release版,这里的@Transcational注解是spring的注解,而不是javax.persitence的注解html

事件生命周期

通常而言,事件的生命周期为事件建立, 事件发布,事件订阅,事件结束.java

事件建立

spring的事件都继承ApplicationEvent,而后添加本身须要的东西spring

以下:数组

public class LoginEvent extends ApplicationEvent implements PointCommon {

    public LoginEvent(Object source) {
        super(source);
    }

    private String activeId;
    private String activeCode="login";
    private Boolean pointFlag;

    public String getActiveId() {
        return activeId;
    }

    public void setActiveId(String activeId) {
        this.activeId = activeId;
    }


    @Override
    public String getActiveCode() {
        return activeCode;
    }

    public Boolean getPointFlag() {
        return pointFlag;
    }

    public void setPointFlag(Boolean pointFlag) {
        this.pointFlag = pointFlag;
    }

    public LoginEvent(Object source, String activeId, String activeCode, Boolean pointFlag) {
        super(source);
        this.activeId = activeId;
        this.pointFlag = pointFlag;
    }
}

这是一个自定的登陆事件,定义了基本的事件信息.架构

事件发布

通常实现ApplicationEventPublisherAware,使用 ApplicationEventPublisher publisher来发布事件. 自定义事件app

public class LoginService implements ApplicationEventPublisherAware {
    private EventService eventService;
    private ApplicationEventPublisher eventPublisher;
  @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.eventPublisher = applicationEventPublisher;
    }
  public void login(HttpServletRequest request...){
    LoginEvent event = new LoginEvent("", "234sd", "login", true);
        publisher.publishEvent(event);
    }
}

订阅事件

实现ApplicationListener<T>接口,T可指定事件的类型,可是要继承ApplicationEvent才行;异步

@Component
public class PointHandler    implements ApplicationListener<LoginEvent >{
                @Override
    public void onApplicationEvent(LoginEvent event) {
    ...
     }

    }

这样就能够处理事件了.jvm

与下面的方式相比,继承EventListener比较笨重.async

@EventListener
    public void handle(PointCommon event) {
        out.println("----斯蒂芬-----");
        out.println(event.getDescription());
    }

没错,直接加上@EventListener就能实现了监听器的功能.ide

事件中断

事件链中同步事件方法出现异常,整个事件链就会出现中断.不继续执行.异步方法无影响.

事件结束

当全部的时间监听器处理完事件以后,事件才算结束,jvm才会执行其余操做.但有异步事件监听器时,只要全部的同步事件监听器处理完事件以后,jvm就会执行其余操做,且异步事件不会对事件源方法产生有效影响.这里能够认为事件已经结束了.

EventListener详解

咱们能够经过注解的condition属性来添加额外的运行时拦截条件,该属性定义了一个SpEL表达式,你须要匹配这个特定事件,才能调用相应的方法.

例如,咱们的通知器能够重写为只有当事件的test属性等于foo时才会调用.

@EventListener(condition = "#event.test == 'foo'")
public void processBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress...
}

每一次SpEL表达式评估都有专门的上下文.下面的表格展现了你在该上下文中能得到的选项,全部你可使用他们进行有条件的事件处理:

Event Spel available metadata

输入图片说明 名称:event 位置:root object 描述:实际的ApplicationEvent(应用事件),例子:#root.event

名称:args 位置:root Object 描述:调用目标使用的参数(数组) 例子:#root.args[0]

名称:argument name 位置:evaluation context 描述:方法中每一个参数的名字.若是因某些缘由这些名字不可得到,这个参数的名称仍能够经过#a<#arg>来获取,这个#arg表明的是参数的下标(从0开始). 例子:#iban或#a0(也可使用#p0或使用#p<#arg>标志做为别名)

记住#root.event容许你访问对应的事件,即便你的方法签名实际上对应的是任意要发布的对象.

若是你由于处理完某个事件后须要发布一个新事件,只须要把方法签名的返回修改成你须要发布的事件的类型,像这样:

@EventListener
public ListUpdateEvent handleBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress and
    // then publish a ListUpdateEvent...
}

该功能不支持异步的监听器;

这个新方法会为每一个该方法处理过的blackListEvent发布一个新的ListUpdateEvent事件.若是你须要发布几个事件,只须要返回这些事件的集合.

异步事件处理方法不支持该特性.

Asynchronous Listeners 异步监听器

若是你想要一个特别的监听器来异步处理事件,简单的使用常规的@Async支持:

@EventListener
@Async
public void processBlackListEvent(BlackListEvent event) {
    // BlackListEvent is processed in a separate thread
}

记住使用异步事件时注意如下几个局限:

  • 1.若是监听器抛出一个异常,它不会传播到调用者那里.查看AsyncUncaughtExceptionHandler以应对更多细节.
  • 2.这些事件监听器没法发送回复.若是你须要发送其余事件做为处理结果,注入ApplicationEventPublisher进行手动发送.

Ordering Listeners 监听器排序

若是你想要一个监听器先于其余调用.只须要在方法参数上添加@Order注解;

@EventListener
@Order(42)
public void processBlackListEvent(BlackListEvent event) {
    // notify appropriate parties via notificationAddress...
}

Generic Events 泛型事件

你可使用泛型更深刻的定义你的事件的结构.思考一个EntityCreatedEvent<T>,其中T是被建立实体的实际类型.你能够建立一个只用来接受Person类的EntityCreatedEvent事件监听器;

@EventListener
public void onPersonCreated(EntityCreatedEvent<Person> event) {
    ...
}

因为类型擦除,只有当事件无缺符合事件监听器拦截的泛型参数,它才工做.(这相似如class PersonCreatedEvent extends EntityCreatedEvent<Person>{...});

在特定的环境下,若是有全部的事件都遵循相同的结构(它多是上面提到的那种事件)会变得至关乏味.这样的话,你可用实现ResolvableTypeProvider来指导运行时环境提供的架构.

public class EntityCreatedEvent<T>
        extends ApplicationEvent implements ResolvableTypeProvider {

    public EntityCreatedEvent(T entity) {
        super(entity);
    }

    @Override
    public ResolvableType getResolvableType() {
        return ResolvableType.forClassWithGenerics(getClass(),
                ResolvableType.forInstance(getSource()));
    }
}

它不只只对ApplicationEvent事件其做用,还适用于你做为事件发布的包含着的任意对象.

同步处理事件

spring的默认事件是同步事件.

@EventListener
    @Transactional
    public void handleEvent(EventEvent event) {
        throw new NullPointerException("测试事务");
//        System.out.println("同步步处理问题!!");
    }
  • 若是方法上加事务注解,则对其方法的事务其做用,如异常回滚.

  • 若是方法上不加事务注解,则对其调用方法的事务无影响

  • 若是方法加上事务注解,根据事务一致性和Transcational的默认事务传播机制,则将该事务纳入到上级方法的事务中,从而影响上级方法的事务.

异步处理事件

开启spring异步支持,才能使用异步事件.开启方法:http://www.javashuo.com/article/p-qvvkxcsr-hd.html

经典代码例子:

@EventListener
    @Async
    @Transactional
    public void handleEvent2(EventEvent event) throws Exception {
        Event event1 = event.getEvent();
        event1.setName("异步事件事务一致性");
        eventService.update(event1);
        System.out.println("异步输出问题!!");
        throw new NullPointerException("测试空指针");
    }
  • 自己事务有效.多是spring版本问题,有人说事务无效,但是在方法体内该事务仍是有效的.
  • 对于事件源所在的方法及其上级来讲,该方法抛出的异常对他们无任何影响.
  • 若是该事件处理方法的业务逻辑容错率低,则不建议使用异步事件.
相关文章
相关标签/搜索