从spring框架中的事件驱动模型出发,优化实际应用开发代码
一、事件起源
相信很多人在使用spring框架进行开发时,都会遇到这样的需求:在spring启动后,立即加载部分资源(例如:spring启动后立刻加载资源初始化到redis中)。当我去解决这个问题时发现,springboot启动过程中会有事件驱动模型的具体实现,共有两种实现:
1)第一种实现,具体代码如下:
import org.springframework.boot.context.event.ApplicationStartedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component; @Component //将监听者交由spring管理
public class StartedListener implements ApplicationListener<ApplicationStartedEvent> {
//实现了ApplicationListener接口并监听ApplicationStartedEvent事件 覆盖当事件发生的方法
@Override
public void onApplicationEvent(ApplicationStartedEvent applicationStartedEvent) {
System.out.println("i am started is ready ...");//只需改为调用具体的业务方法即可
}
}
2)第二种实现,具体代码如下:
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component; @Component //将监听者交由spring管理
public class CommandListener implements CommandLineRunner {
//实现了CommandLineRunner接口并覆盖run()方法
@Override
public void run(String... args) throws Exception {
System.out.println("i am commandline runner is ready ...");//将输出改为调用具体的业务方法即可
}
}
第一种和第二种之间存在联系,稍后会介绍两者之间的联系和区别,我们更加直观的可以看到第一种方法中的具体实现。
发现,1、只需要实现ApplicationListener这个接口,2、并声明泛型-监听的具体事件,3、然后覆盖onApplicationEvent()方法三步走战略即可。
还发现一个优势:如果一个将一个具体业务形象化为一个“事件”,例如:用户注册或者是用户下订单,这种具体的业务如果改造成事件的话,就可以实现解耦,方便以后添加用户注册或者下订单后的操作,举个栗子:
买家用户下订单 -> 1、扣去商品表中的库存数量 -> 2、短信通知买家用户下单成功 -> 3、通知商家用户物流发货 -> 4、微信通知买家下单成功 等 后续操作
如果现在,我们需要对该业务代码进行变化,我们需要增加一个需求变化:需要在买家用户下订单成功后,给买家用户进行:小程序通知,当你面对这个需求时,很直观的感觉应该是去直接在具体的业务代码下,加上一个小程序通知即可,可是忽略了之前可能有对应的逻辑操作,这个时候可能还需要花费一些时间,把之前的代码给看清楚,避免自己维护代码时出现问题。
这个时候如果我们直接去用事件驱动模型这种方式,来思考我们的业务,完全可以把买家用户下订单这个作为一个事件存在,这样其他的操作就是并行的操作,可以独立存在,如果需要直接添加就可以了。

我们先来看一下原来未改造之前的代码:
import com.shuwen.demo.service.OrderService;
import org.springframework.stereotype.Service; @Service
public class OrderServiceImpl2 implements OrderService { @Override
public void saveOder() {
//1、创建订单
System.out.println("订单创建成功");
//2、发送短信通知
System.out.println("用户您好,恭喜您抢购成功~来自短信");
//3、发送微信通知
System.out.println("用户您好,恭喜您抢购成功~来自微信");
//4、发送小程序通知
System.out.println("用户您好,恭喜您抢购成功~来自小程序");
}
}
再来对比一下改造之后的代码:
a)首先声明一个事件:OrderCreateEvent
import org.springframework.context.ApplicationEvent;
//继承ApplicationEvent即可
public class OrderCreateEvent extends ApplicationEvent {
//默认覆盖
public OrderCreateEvent(Object source) {
super(source);
}
}
b)当买家下订单成功后,就需要创建并发布一个事件
import com.shuwen.demo.event.OrderCreateEvent;
import com.shuwen.demo.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service; @Service
public class OrderServiceImpl implements OrderService {
@Autowired
ApplicationContext applicationContext;
@Override
public void saveOder() {
//1、创建订单
System.out.println("订单创建成功");
//创建订单创建的事件
OrderCreateEvent orderCreateEvent = new OrderCreateEvent("order create is success");
//利用applicationContext将事件发布
applicationContext.publishEvent(orderCreateEvent);
//2、发送短信通知
//3、发送微信通知
//4、发送小程序通知
}
}
c)我们就可以模仿spring进行监听事件发生,然后完成后续操作
1、进行短信通知
import com.shuwen.demo.event.OrderCreateEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component; @Component //交给spring管理 实现接口并监听事件源
public class MessageListener implements ApplicationListener<OrderCreateEvent> {
@Async //可以异步执行
@Override //覆盖方法 即可实现具体的业务操作
public void onApplicationEvent(OrderCreateEvent orderCreateEvent) {
System.out.println(orderCreateEvent.getSource()+",message is sending");
}
}
2、进行微信通知
import com.shuwen.demo.event.OrderCreateEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component; @Component
public class WeixinListener implements ApplicationListener<OrderCreateEvent> {
@Async
@Override
public void onApplicationEvent(OrderCreateEvent orderCreateEvent) {
System.out.println(orderCreateEvent.getSource()+",weixin is sending");
}
}
3、这时候如果新需求是增加:小程序通知,我们直接添加一个监听者 即可。
import com.shuwen.demo.event.OrderCreateEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component; @Component
public class LittleProListener implements ApplicationListener<OrderCreateEvent> {
@Async
@Override
public void onApplicationEvent(OrderCreateEvent orderCreateEvent) {
System.out.println(orderCreateEvent.getSource()+",xiao cheng xu is sending ...");
}
}
能够写出这样代码的人,你要珍惜他,因为当你维护他的项目时,很轻松,不用担心出现问题,也不必费时看原来的逻辑。
这时候,可能就有人有这样的问题,这如果存在逻辑顺序怎么处理这个问题呢?

如果存在这种逻辑顺序,我们又该如何处理,spring自然给我们提供了方法,哈哈,这就是spring的魅力~
它的名字很有意思:SmartApplicationListener smart是不是一下就聪明、伶俐了很多~
我们先看看它是如何实现具体的业务的,方便大家更好的理解:
a)微信通知
import com.shuwen.demo.event.OrderCreateEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component; @Component //实现smart接口
public class SmsListener implements SmartApplicationListener {
@Override //声明支持的事件类型
public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
return aClass == OrderCreateEvent.class;
} @Override //参数的类型
public boolean supportsSourceType(Class<?> sourceType) {
return sourceType == String.class;
}
//order 确定优先级的顺序 数值越小 优先级越高
@Override //优先级数值
public int getOrder() {
return 5;
} @Override //具体的业务实现
public void onApplicationEvent(ApplicationEvent applicationEvent) {
System.out.println(applicationEvent.getSource()+",sms is sending smart");
}
}
b)微信通知
import com.shuwen.demo.event.OrderCreateEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component; @Async
@Component //实现smart接口
public class WeixinListenerSmart implements SmartApplicationListener {
@Override //声明支持的事件类型
public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
return aClass == OrderCreateEvent.class;
} @Override //声明支持的参数类型
public boolean supportsSourceType(Class<?> sourceType) {
return sourceType == String.class;
} @Override //声明优先级的大小 越小越高
public int getOrder() {
return 2;
} @Override //覆盖方法 实现具体的业务方法
public void onApplicationEvent(ApplicationEvent applicationEvent) {
System.out.println(applicationEvent.getSource()+",weixin is sending smart");
}
}
c)小程序通知
import com.shuwen.demo.event.OrderCreateEvent;
import org.springframework.context.ApplicationEvent;
import org.springframework.context.event.SmartApplicationListener;
import org.springframework.stereotype.Component; @Component //实现smart接口
public class LittleProListenerSmart implements SmartApplicationListener {
@Override //声明支持的事件类型
public boolean supportsEventType(Class<? extends ApplicationEvent> aClass) {
return aClass == OrderCreateEvent.class;
} @Override //声明支持的参数类型
public boolean supportsSourceType(Class<?> sourceType) {
return sourceType == String.class;
} @Override //声明该监听者的优先级
public int getOrder() {
return 6;
} @Override //具体的业务实现
public void onApplicationEvent(ApplicationEvent applicationEvent) {
System.out.println(applicationEvent.getSource()+",xiao cheng xu is sending smart");
}
}
大家一定要记得清楚哟,order值越小,优先级越高。
那么下面,我们要去追根溯源一下,看看spring的事件驱动模式的具体实现及相关的原理,让我们有一个更加深入的理解。
二、spring提供的事件驱动模型/观察者抽象
我们首先需要了解一下spring的体系结构图:
 public void publishEvent(ApplicationEvent event) {
        //省略部分代码
        }
        getApplicationEventMulticaster().multicastEvent(event);
        if (this.parent != null) {
            this.parent.publishEvent(event);
        }
 }
我们常用的ApplicationContext都继承自AbstractApplicationContext,如ClassPathXmlApplicationContext、XmlWebApplicationContext等。所以自动拥有这个功能。
b) ApplicationContext自动到本地容器里找一个名字为”“的ApplicationEventMulticaster实现,如果没有自己new一个SimpleApplicationEventMulticaster。其中SimpleApplicationEventMulticaster发布事件的代码如下:
public void multicastEvent(final ApplicationEvent event) {
        for (final ApplicationListener listener : getApplicationListeners(event)) {
            Executor executor = getTaskExecutor();
            if (executor != null) {
                executor.execute(new Runnable() {
                    public void run() {
                        listener.onApplicationEvent(event);
                    }
                });
            }
            else {
                listener.onApplicationEvent(event);
            }
        }
}
大家可以看到如果给它一个executor(java.util.concurrent.Executor),它就可以异步支持发布事件了。否则就是通过发送。
所以我们发送事件只需要通过ApplicationContext.publishEvent即可,没必要再创建自己的实现了。除非有必要。
3)监听者,具体实现者: ApplicationListener, 其继承自JDK的EventListener,JDK要求所有监听器将继承它;
ApplicationListener接口:
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
    void onApplicationEvent(E event);
}
其只提供了onApplicationEvent方法,我们需要在该方法实现内部判断事件类型来处理,也没有提供按顺序触发监听器的语义,所以Spring提供了另一个接口,SmartApplicationListener:
public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
   //支持的事件类型 
  boolean supportsEventType(Class<? extends ApplicationEvent> var1);
  //支持的参数类型
    default boolean supportsSourceType(@Nullable Class<?> sourceType) {
        return true;
    }
  //优先级的大小
    default int getOrder() {
        return 2147483647;
    }
}
时光匆匆不留人,望你我皆有所收获。
从spring框架中的事件驱动模型出发,优化实际应用开发代码的更多相关文章
- 再析在spring框架中解决多数据源的问题
		在前面我写了<如何在spring框架中解决多数据源的问题>,通过设计模式中的Decorator模式在spring框架中解决多数据源的问题,得到了许多网友的关注.在与网友探讨该问题的过程中, ... 
- Spring框架中  配置c3p0连接池 完成对数据库的访问
		开发准备: 1.导入jar包: ioc基本jar jdbcTemplate基本jar c3p0基本jar 别忘了mysql数据库驱动jar 原始程序代码:不使用配置文件方式(IOC)生成访问数据库对象 ... 
- Spring框架中ModelAndView、Model、ModelMap区别
		原文地址:http://www.cnblogs.com/google4y/p/3421017.html SPRING框架中ModelAndView.Model.ModelMap区别 注意:如果方法 ... 
- Spring框架中的定时器 使用和配置
		Spring框架中的定时器 如何使用和配置 转载自:<Spring框架中的定时器 如何使用和配置>https://www.cnblogs.com/longqingyang/p/554543 ... 
- 细说shiro之五:在spring框架中集成shiro
		官网:https://shiro.apache.org/ 1. 下载在Maven项目中的依赖配置如下: <!-- shiro配置 --> <dependency> <gr ... 
- 【Spring】8、Spring框架中的单例Beans是线程安全的么
		看到这样一个问题:spring框架中的单例Beans是线程安全的么? Spring框架并没有对单例bean进行任何多线程的封装处理.关于单例bean的线程安全和并发问题需要开发者自行去搞定.但实际上, ... 
- Spring5源码解析-Spring框架中的单例和原型bean
		Spring5源码解析-Spring框架中的单例和原型bean 最近一直有问我单例和原型bean的一些原理性问题,这里就开一篇来说说的 通过Spring中的依赖注入极大方便了我们的开发.在xml通过& ... 
- Spring框架中IoC(控制反转)的原理(转)
		原文链接:Spring框架中IoC(控制反转)的原理 一.IoC的基础知识以及原理: 1.IoC理论的背景:在采用面向对象方法设计的软件系统中,底层实现都是由N个对象组成的,所有的对象通过彼此的合作, ... 
- spring框架中的@Import注解
		spring框架中的@Import注解 Spring框架中的@Import注解 在之前的文章中,作者介绍了Spring JavaConfig. 这是除了使用传统的XML文件之外,spring带来的新的 ... 
随机推荐
- JAVA小记 (1)
			JVM: Java虚拟机 JVM个数取决于同时执行的程序个数 JDK:JAVA 开发工具包 Java利用JVM实行跨平台 JRE:Java运行环境 JavaSE:企业版 GC:垃圾回收机制 命名规范 ... 
- 云计算一:VMware workstation的安装和使用教程
			VMware workstation的安装和使用教程 一.VMware 安装 1.从网上找到VMware的安装包以及要安装的映像文件,下载到本地,然后备份一份存储到百度云盘. 链接:http://pa ... 
- 设计模式的征途—17.模板方法(Template Method)模式
			在现实生活中,很多事情都需要经过几个步骤才能完成,例如请客吃饭,无论吃什么,一般都包含:点单.吃东西.买单等几个步骤,通常情况下这几个步骤的次序是:点单=>吃东西=>买单.在这3个步骤中, ... 
- HBuilder真机联调、手机运行
			第一步:先确认手机是否连接上 未连接状态 如下图所示为已连接状态 导致手机未成功连接的原因: (1)手机与电脑未用USB数据线连接(嘿嘿,这一部大家估计都做到了,可略过) (2)电脑上需要安装电脑版的 ... 
- Python_跟随目标主机IP变换
			''' 为了防止黑客攻击或者负载均衡,会经常变换主机,这样同一个域名在不同时间可能会对应不同的IP地址,在这种情况下可以通过 socket模块的gethostbyname()函数来实时获取目标主机的I ... 
- 创建第一个core项目(netCore学习笔记1)
			1.安装 core和netFramework其实是相对独立的,但是core的IDE是在vs2017才开始支持,而vs2017的安装环境必须搭配.net4.6,所以: Step1:安装.net4.6 S ... 
- ORC文字识别软件破解版
			下载地址:http://pan.baidu.com/s/1bnCiXdl 点击 然后可以免费用了ABBYY了!! 
- 指针超强汇总(谨记优先级:() > [] > *)
			参考:http://blog.chinaunix.net/uid-20120277-id-5760985.html C语言所有复杂的指针声明,都是由各种声明嵌套构成的.如何解读复杂指针声明呢?右左法则 ... 
- SSM-SpringMVC-18:SpringMVC中参数自动装配
			------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- 在处理方法中,参数写上之后,只要符合特定规则,就可以自动装配 首先 其次是:自定义的参数的自动装配: 案例如 ... 
- Java 8 Documentation Download
			Java API 下载方法 https://www.oracle.com/index.html https://www.oracle.com/java/technologies/java-se.htm ... 
