事件Event监听相关——spring的事件监听相关

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/itwxming/article/details/100164883

应用场景:

事件驱动模型简介

事件驱动模型也就是我们常说的观察者,或者发布-订阅模型。理解它的几个关键点:

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;
    }
}
  1. supportsEventType:用于指定支持的事件类型,只有支持的才调用onApplicationEvent;
  2. supportsSourceType:支持的目标类型,只有支持的才调用onApplicationEvent;
  3. 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();
}

猜你喜欢

转载自blog.csdn.net/itwxming/article/details/100164883
今日推荐