spring 事件

本文详细介绍了Spring框架中的事件处理机制,包括自定义事件创建、事件发布、事件订阅及处理过程。探讨了同步与异步事件处理的区别,并给出了具体的代码实例。

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

事件生命周期

一般而言,事件的生命周期为事件创建, 事件发布,事件订阅,事件结束.

事件创建

spring的事件都继承ApplicationEvent,然后添加自己需要的东西

如下:

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来发布事件. 自定义事件

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) {
    ...
     }

    }

这样就可以处理事件了.

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

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

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

事件中断

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

事件结束

当所有的时间监听器处理完事件之后,事件才算结束,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异步支持,才能使用异步事件.开启方法:https://my.oschina.net/u/1590027/blog/1511296

经典代码例子:

 @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版本问题,有人说事务无效,可是在方法体内该事务还是有效的.
  • 对于事件源所在的方法及其上级来说,该方法抛出的异常对他们无任何影响.
  • 如果该事件处理方法的业务逻辑容错率低,则不建议使用异步事件.

转载于:https://my.oschina.net/u/1590027/blog/1511379

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值