spring学习笔记(16)趣谈spring 事件机制[2]:多监听器流水线式顺序处理

上一篇我们使用到的ApplicationListener是无序的,结合异步调度它能满足了我们的大部分应用场景,但现在我们来个另类的需求,我们来模拟一条作业调度流水线,它不能异步,必须按照先后次序执行不同的任务才能得到我们的最终结果。
需求示例:现在假如华中科技大学的小白想要为它的智能机器人作品申报国家创新奖,需要经过学校、省级创新科研机构、国家创新科研机构逐层审核。我们尝试通过事件来实现,核心就在监听器实现SmartApplicationListener接口。示例如下:

1. 配置事件发布者小白:

public class XiaoBai implements ApplicationContextAware {

    private ApplicationContext applicationContext;//底层事件发布者

    public void reportWorks(){//申报作品
        AuditEvent auditEvent = new AuditEvent(this);
        applicationContext.publishEvent(auditEvent);
        //小白获取期待已久的最终结果
        System.out.println("最终审核结果:" + auditEvent.getStatus() + "——" + auditEvent.getAdvice());
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext = applicationContext;
    }
}

关于上述配置可参考我的上一篇文章《spring学习笔记(13)趣谈spring 事件:实现业务逻辑解耦,异步调用提升用户体验》

2. 配置审核事件源

public class AuditEvent extends ApplicationEvent {

    private Boolean status ; //当前申报状态
    private String work;//申报作品
    private String advice;//当前申报意见

    public AuditEvent(Object  source) {
        super(source);
        status = true;//初始窗台
        advice = "尚未审核";
    }
    //ignore getter and setter
}

3. 配置事件监听器

我们实现需求的核心部分来了,先上代码

/******************学校审核监听器******************/
public class SchoolListener implements  SmartApplicationListener  {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        System.out.println("获取当前的申报状态为:"+((AuditEvent)event).getStatus() + "——" + ((AuditEvent)event).getAdvice());
        ((AuditEvent)event).setStatus(true);
        ((AuditEvent)event).setAdvice("学校审核意见:有创意,非常棒!");
    }

    @Override
    public int getOrder() {
        return 1;//获取学校监听(审核)的优先级
    }

    @Override//这是我们的监听器智能所在之一,能够根据事件类型动态监听
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return eventType == AuditEvent.class;
    }

    @Override//这是我们的监听器智能所在之二,能够根据事件发布者类型动态监听
    public boolean supportsSourceType(Class<?> sourceType) {
        return sourceType == XiaoBai.class;
    }
}
/******************省级审核监听器******************/
public class ProvinceListener implements  SmartApplicationListener  {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if(((AuditEvent)event).getStatus()){//如果上层审核通过
            System.out.println("获取当前的申报状态为:"+((AuditEvent)event).getStatus() + "——" + ((AuditEvent)event).getAdvice());
            ((AuditEvent)event).setStatus(true);
            ((AuditEvent)event).setAdvice("省级审核意见:还行,通过吧!");
        }
    }

    @Override
    public int getOrder() {
        return 2;
    }

    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return eventType == AuditEvent.class;
    }

    @Override
    public boolean supportsSourceType(Class<?> sourceType) {
        return sourceType == XiaoBai.class;
    }

}

/******************国家级审核监听器******************/
public class CountryListener implements  SmartApplicationListener  {

    @Override
    public void onApplicationEvent(ApplicationEvent event) {
        if(((AuditEvent)event).getStatus()){//如果上层审核通过
            System.out.println("获取当前的申报状态为:"+((AuditEvent)event).getStatus() + "——" + ((AuditEvent)event).getAdvice());
            ((AuditEvent)event).setStatus(true);
            ((AuditEvent)event).setAdvice("国家审核意见:一般般,勉强通过吧!");
        }
    }

    @Override
    public int getOrder() {
        return 3;
    }

    @Override
    public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
        return eventType == AuditEvent.class;
    }

    @Override
    public boolean supportsSourceType(Class<?> sourceType) {
        return sourceType == XiaoBai.class;
    }

}

在这里,我们的实例为了方便演示而简化配置。我们的发布源支持类型是小白,根据更实际的需求,我们可以让小白继承Student类,然后让sourceType只要是Student的子类即可,这样就能满足任何继承了Student的学生都能申报了。
在实现了SmartApplicationListener的监听器中,我们通过重写GetOrder方法来修改不同监听器的顺序,优先级越小,则越先被调用。通过配置不同的优先级,且让监听器之间阻塞调用。我们就能实现流水线式的有序事件调用,这在实际应用场景中还是蛮有意义的

5. 在IOC容器中注册监听器

<bean id="schoolListener" class="test.event2.SchoolListener" />
<bean id="provinceListener" class="test.event2.ProvinceListener" />
<bean id="countryListener" class="test.event2.CountryListener" />

<bean id="xiaoBai" class="test.event2.XiaoBai" /><!-- 测试时注入使用 -->

6. 测试方法

public class MainTest {
    public static void main(String args[]) throws InterruptedException{
        ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:test/event2/event.xml");
        XiaoBai xiaobai = (XiaoBai) ac.getBean("xiaoBai");
        xiaobai.reportWorks();//小白要开始申报项目啦
    }
}

调用测试代码,我们得到运行结果:
获取当前的申报状态为:true——尚未审核
获取当前的申报状态为:true——学校审核意见:有创意,非常棒!
获取当前的申报状态为:true——省级审核意见:还行,通过吧!
最终审核结果:true——国家审核意见:一般般,勉强通过吧!

于是,小白的作品终于评上了国家创新科技奖了,这让他高兴了好一阵子。

源码下载

本实例源码可到我的github仓库https://github.com/jeanhao/spring下的event2文件夹下载

时间: 2024-10-30 21:50:40

spring学习笔记(16)趣谈spring 事件机制[2]:多监听器流水线式顺序处理的相关文章

spring学习笔记(13)基于Schema配置AOP详解

基于Schema配置入门实例 除了基于@AspectJ注解的形式来实现AOP外,我们还可以在IOC容器中配置.先来看看一个常见的应用场景,在我们的web项目中,我们需要为service层配置事务,传统的做法是在每个业务逻辑方法重复下面配置中: Created with Raphaël 2.1.0程序开始1. 获取DAO层封装好的数据库查询API,如HIbernate中的SessionFactory/Session和mybatis中的xxxMapper2. 开启事务3. 根据入参查询数据库完成相应

spring学习笔记(10)@AspectJ研磨分析[1]入门、注解基本介绍

@AspectJ准备 AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件. 在使用AspectJ之前,我们需要导入aspectJ相应的jar包,可到我的资源页http://download.csdn.net/detail/qwe6112071/9468329 中下载,而如果使用maven则可直接在pom.xml中加入如下代码: <dependency> <groupId>o

spring学习笔记(19)mysql读写分离后端AOP控制实例

在这里,我们接上一篇文章,利用JNDI访问应用服务器配置的两个数据源来模拟同时操作不同的数据库如同时操作mysql和oracle等.实际上,上个例子可能用来模拟mysql数据库主从配置读写分离更贴切些.既然如此,在本例中,我们就完成读写分离的模拟在web端的配置实例. 续上次的例子,关于JNDI数据源的配置和spring datasource的配置这里不再重复.下面着重加入AOP实现DAO层动态分库调用.可先看上篇文章<spring学习笔记(18)使用JNDI模拟访问应用服务器多数据源实例 >

spring学习笔记(21)编程式事务配置,service层概念引入

访问数据库事务导入 在我之前的文章<spring学习笔记(19)mysql读写分离后端AOP控制实例>中模拟数据库读写分离的例子,在访问数据库时使用的方法是: public <E> E add(Object object) { return (E) getSessionFactory().openSession().save(object); } 通过直接开启session而后保存对象.查询数据等操作,是没有事务的.而如果我们的项目规模变大,业务逻辑日益复杂,我们在一个方法中进行大

Spring学习笔记之依赖的注解(2)

Spring学习笔记之依赖的注解(2) 1.0 注解,不能单独存在,是Java中的一种类型 1.1 写注解 1.2 注解反射 2.0 spring的注解 spring的 @Controller@Component@Service//更多典型化注解,但是@Controller@Service建议使用 @service("personService")可以代替set get 方法,@Resource(name=personDao) @Autowired//按照类型匹配 @Qualifier

Spring学习笔记之aop动态代理(3)

Spring学习笔记之aop动态代理(3) 1.0 静态代理模式的缺点: 1.在该系统中有多少的dao就的写多少的proxy,麻烦 2.如果目标接口有方法的改动,则proxy也需要改动. PersonDao.java public interface PersonDao { public void savePerson(); } PersonDaoImpl.java public class PersonDaoImpl implements PersonDao{ public void save

Spring学习笔记2之表单数据验证、文件上传实例代码_java

在上篇文章给大家介绍了Spring学习笔记1之IOC详解尽量使用注解以及java代码,接下来本文重点给大家介绍Spring学习笔记2之表单数据验证.文件上传实例代码,具体内容,请参考本文吧! 一.表单数据验证 用户注册时,需要填写账号.密码.邮箱以及手机号,均为必填项,并且需要符合一定的格式.比如账号需要32位以内,邮箱必须符合邮箱格式,手机号必须为11位号码等.可以采用在注册时验证信息,或者专门写一个工具类用来验证:来看下在SpringMVC中如何通过简单的注释实现表单数据验证. 在javax

Spring学习笔记3之消息队列(rabbitmq)发送邮件功能_java

rabbitmq简介: MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们.消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术.排队指的是应用程序通过 队列来通信.队列的使用除去了接收和发送应用程序同时执行的要求.其中较为成熟的MQ产品有IBM WEBSPHERE MQ. 本节的内容是用户注册时,将邮

javascript学习笔记_浅谈基础语法,类型,变量_基础知识

基础语法.类型.变量 非数字值的判断方法:(因为Infinity和NaN他们不等于任何值,包括自身) 1.用x != x ,当x为NaN时才返回true; 2.用isNaN(x) ,当x为NaN或非数字值时,返回true; 3.用isFinity(x),在x不是NaN.Infinity.-Infinity时返回true; 虽然(字符串.数字.布尔值)不是对象,他们的属性是只读的,但也可以像操作对象一样来引用他们的属性和方法,原理: javascript构造一个(String.Number.Boo