事件Event监听相关——spring的事件监听相关
应用场景:
事件驱动模型简介
事件驱动模型也就是我们常说的观察者,或者发布-订阅模型。理解它的几个关键点:
1、首先是对象间的一对多的关系;最简单的如交通信号灯,信号灯是目标(一方),行人注视着信号灯(多方);
2、当目标发送改变(发布),观察者(订阅者)就可以接收到改变;
3、观察者如何处理(如行人如何走,是快走/慢走/不走,目标不会管的),目标无需干涉;所以就松散耦合了它们之间的关系。
比分最常见的用户注册之后可能需要做很多操作:
1、加积分
2、发优惠券
3、创建初始化数据等…………
问题:
UserService和其他Service耦合严重,增删功能比较麻烦;
有些功能可能需要调用第三方系统,如增加积分/索引用户,速度可能比较慢,此时需要异步支持。
所以需要解耦这些Service之间的依赖关系。事件监听就是一种最佳的解决方案。
如何实现呢?面向接口编程(即面向抽象编程),而非面向实现。即事件和动作可以定义为接口,这样它俩的依赖是最小的。
在Java中接口还一个非常重要的好处:接口是可以多实现的,类/抽象类只能单继承,所以使用接口可以非常容易扩展新功能。
一、最简单基本的事件机制:
java中的事件机制的参与者有3种角色:
1.event object:事件状态对象,集成java.util.EventObject。用于listener的相应的方法之中,作为参数,一般存在于listerner的方法之中。
2.event listener:对每个明确的事件的发生,都相应地定义一个明确的Java方法。这些方法都集中定义在事件监听者(EventListener)接口中,这个接口要实现 java.util.EventListener。 实现了事件监听者接口中一些或全部方法的类就是事件监听者。
3.event source:具体的事件源,比如说,注册事件,值改变事件,事件源就是event source,要想对这些事件进行响应,就需要注册特定的listener。
示例代码:
1、被监控值改变的对象:
package my.mark.mybaibaoxiang.eventDemo.easyEvent;
/**
* @author twotiger-wxm.
* @date 2019-9-11.
*/
public class Door {
/**==================单例模式begin=================*/
private final static Door INSTANCE = new Door(new DoorStateListener(),new DoorNameListener());
//私有化构造函数,不让外部调用。
private Door(){}
private Door(DoorStateListener stateListener,DoorNameListener nameListener){
this.stateListener= stateListener;
this.nameListener= nameListener;
}
public static Door getInstance(){
return INSTANCE;
}
/**==================单例模式end=================*/
private String state = "";
private String name = "";
private DoorStateListener stateListener;
private DoorNameListener nameListener;
public void setState(String newValue) {
if (state != newValue) {
state = newValue;
if (stateListener != null){
//注意参数传递
DoorEvent event = new DoorEvent(this, "state",newValue);
stateListener.doorEvent(event);
}
}
}
public void setName(String newValue) {
if (name != newValue) {
name = newValue;
if (nameListener != null){
DoorEvent event = new DoorEvent(this,"name", newValue);
nameListener.doorEvent(event);
}
}
}
public void setStateListener(DoorStateListener stateListener) {
this.stateListener = stateListener;
}
public void setNameListener(DoorNameListener nameListener) {
this.nameListener = nameListener;
}
public String getState() {
return state;
}
public String getName() {
return name;
}
}
2、事件传递对象
package my.mark.mybaibaoxiang.eventDemo.easyEvent;
import java.util.EventObject;
/**
* @author twotiger-wxm.
* @date 2019-9-11.
*/
public class DoorEvent extends EventObject {
private static final long serialVersionUID = 6496098798146410884L;
private final String key;
private final String value;
/**source存放Object对象,传递业务数据对象。*/
public DoorEvent(Object source,String key,String value) {
super(source);
this.key = key;
this.value = value;
}
public String getValue() {
return value;
}
public String getKey() {
return key;
}
}
3、两个监听值改变了的监听类
package my.mark.mybaibaoxiang.eventDemo.easyEvent;
import java.util.EventListener;
/**
* @author twotiger-wxm.
* @date 2019-9-11.
*/
public class DoorNameListener implements EventListener {
public void doorEvent(DoorEvent event) {
Door door = (Door) event.getSource();
System.out.println("I got a new name,named \"" + door.getName() + "\"");
}
}
package my.mark.mybaibaoxiang.eventDemo.easyEvent;
import java.util.EventListener;
/**
* @author twotiger-wxm.
* @date 2019-9-11.
*/
public class DoorStateListener implements EventListener {
public void doorEvent(DoorEvent event) {
if (event.getValue() != null && event.getValue().equals("open")) {
System.out.println("门1打开");
} else {
System.out.println("门1关闭");
}
}
}
4、事件触发来源测试类
package my.mark.mybaibaoxiang.eventDemo.easyEvent;
/**
* 最简单的事件监听模式(某个对象的值改变后做相应操作。)
* @author twotiger-wxm.
* @date 2019-9-11.
*/
public class EventTest {
public static void main(String[] args) {
Door door = Door.getInstance();
// 开门
door.setState("open");
System.out.println("我已经进来了");
// 关门
door.setState("close");
// 改名
door.setName("dengzy"); }
}
二、spring的事件Event相关
zuul等很多优秀的源码用到了EventListener。
spring的事件监听有三个部分组成,事件(ApplicationEvent)、监听器(ApplicationListener)和事件发布操作。
1、事件类需要继承ApplicationEvent,代码如下:
package my.mark.mybaibaoxiang.eventDemo.spring;
import org.springframework.context.ApplicationEvent;
/**
* 自定义spring事件,自定义的业务事件,在自己的业务逻辑里使用:发布、监听执行。
*/
public class WarnEvent extends ApplicationEvent {
private static final long serialVersionUID = -7344918861434285999L;
/**
* 以下自定义参数传需要的业务参数,到监听后执行。
*/
private String type;
private String target;
private String content;
public WarnEvent(Object source, String type, String target, String content) {
super(source);
this.type = type;
this.target = target;
this.content = content;
}
public String getType() {
return type;
}
public String getTarget() {
return target;
}
public String getContent() {
return content;
}
}
事件类是一种很简单的pojo,除了需要继承ApplicationEvent也没什么了,这个类有一个构造方法需要super。super使该事件类中可携带任何数据,比方唯一标识,或者事件源业务数据对象。通过source得到事件源。
2、事件监听类
事件监听器需要实现ApplicationListener接口,这是个泛型接口,泛型类类型就是事件类型,其次需要是spring容器托管的bean,所以这里加了@component,只有一个方法,就是onApplicationEvent。
@Component
public class HelloEventListener implements ApplicationListener<HelloEvent> {
private static final Logger logger = LoggerFactory.getLogger(HelloEventListener.class);
@Override
public void onApplicationEvent(HelloEvent event) {
logger.info("receive {} say hello!",event.getName());
}
}
ApplicationListener 接口的定义如下:
@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {
/**
* Handle an application event.
* @param event the event to respond to
*/
void onApplicationEvent(E event);
}
它是一个泛型接口,泛型的类型必须是 ApplicationEvent 及其子类,只要实现了这个接口,那么当容器有相应的事件触发时,就能触发 onApplicationEvent 方法。
我们可以直接去监听ApplicationEvent去监听spring的所有事件:
@Component
public class MyApplicationListener implements ApplicationListener<ApplicationEvent> {
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("事件触发:"+event.getClass().getName());
}
}
或者直接用注解将某个方法变成监听处理器:
@Async
@EventListener
public void send(WarnEvent event) {
try {
logger.info("===================WarnEvent走了");
/*SendService sendService = factory.getSendService(event.getType());
sendService.send(event.getTarget(), event.getContent());*/
} catch (Exception e) {
logger.error("消息发送失败! cause by:" + e.getMessage(), e);
}
}
EventListener这个注解其实可以接受参数来表示事件源的,有两个参数classes和condition,顾明思议前者是表示哪一个事件类,后者是当满足什么条件是会调用该方法,但其实都可以不用用到,直接在方法上写参数HelloEvent就行。
3、事件发布操作
applicationContext.publishEvent(new WarnEvent(UUID.randomUUID(), type, target, content));
要获取应用上下文applicationContext,可实现implements ApplicationContextAware,通过接口方法获取。
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
事件发布操作和事件监听后操作默认同步,但如果给监听方法加spring的异步线程注解@Async使用默认线程池或者指定线程池,也可以让变成异步。
三、Spring事件驱动
spring的事件驱动最大的特征是监听器的添加方式,spring一般的添加方式是实现ApplicationListener
接口,然后把实现类注册为Bean,spring的上下文初始化的时候会自动扫描路径下的实现ApplicationListener
接口的类,然后添加到监听器集合里面,发布事件的时候会通知所有的监听器(同老虎的监听设计一样),这一切要依赖spring的容器管理Bean的功能。
四、监听事件顺序问题
Spring 提供了一个SmartApplicationListener类,可以支持listener之间的触发顺序,普通的ApplicationListener优先级最低(最后触发)。可以对同一个事件多个监听需顺序执行下使用。
@Component
public class MyListener implements SmartApplicationListener {
private volatile AtomicBoolean isInit = new AtomicBoolean(false);
@Override
public boolean supportsEventType(Class<? extends ApplicationEvent> eventType) {
return eventType == ContextRefreshedEvent.class;
}
@Override
public boolean supportsSourceType(Class<?> sourceType) {
return sourceType == String.class;//或者直接return true;看需求有没有该限制要求。一般上边那个方法限制事件类型就满足了。
}
@Override
public void onApplicationEvent(ApplicationEvent event) {
System.out.println("孙悟空在沙僧之后收到新的内容:" + event.getSource());
}
//值越小,就先触发
@Override
public int getOrder() {
return 2;
}
}
- supportsEventType:用于指定支持的事件类型,只有支持的才调用onApplicationEvent;
- supportsSourceType:支持的目标类型,只有支持的才调用onApplicationEvent;
- getOrder:即顺序,越小优先级越高
SmartApplicationListener的源码:
public interface SmartApplicationListener extends ApplicationListener<ApplicationEvent>, Ordered {
/**
* Determine whether this listener actually supports the given event type.
* @param eventType the event type (never {@code null})
*/
boolean supportsEventType(Class<? extends ApplicationEvent> eventType);
/**
* Determine whether this listener actually supports the given source type.
* <p>The default implementation always returns {@code true}.
* @param sourceType the source type, or {@code null} if no source
*/
default boolean supportsSourceType(@Nullable Class<?> sourceType) {
return true;//默认true。
}
/**
* Determine this listener's order in a set of listeners for the same event.
* <p>The default implementation returns {@link #LOWEST_PRECEDENCE}.
*/
@Override
default int getOrder() {
return LOWEST_PRECEDENCE;//默认排序值最大,优先级最低。取自Ordered接口。
}
}
public interface Ordered {
int HIGHEST_PRECEDENCE = -2147483648;
int LOWEST_PRECEDENCE = 2147483647;
int getOrder();
}
还没有评论,来说两句吧...