理解Spring事件机制的前因后果

今天咱们聊聊Spring中的事件,你也可以理解成消息,这是典型的观察者模式的实现,其主要目的是用于项目解耦。
Spring的事件默认是同步方式,不是所有的事件都是异步!!!

事件三要素:

  • 发布者
  • 订阅者(监听器)
  • 事件主体

1.事件监听任务

1.1 通过实现接口
//定义事件主体
@Data
public class CurrentUser extends ApplicationEvent {

    private String name;
    private int age;

    public CurrentUser(Object source, String name, int age){
        super(source);
        this.name = name;
        this.age = age;
    }
}


//通过实现接口的方式监听事件
@Component
public class SubscriberClass implements ApplicationListener<CurrentUser> {
    private static final Logger log = LoggerFactory.getLogger(SubscriberClass.class);
    @Override
    public void onApplicationEvent(CurrentUser currentUser) {
        log.info("SubscriberClass + onApplicationEvent + " + currentUser.getName());
    }
}

1.2通过@EventListener注解
@Component
public class SubscriberClass{

    private static final Logger log = LoggerFactory.getLogger(SubscriberClass.class);

    @EventListener
    public void handleCurrentUserThird(CurrentUser currentUser){
        log.info("SubscriberClass + handleCurrentUser + " + currentUser.getName());
    }
}
1.3 监听器处理多个事件类型

如果你的项目需要一个监听器处理一种大的类型,你可以定义抽象类继承ApplicationEvent , 然后将具体的事件类型继承该类型。

这时候你可以在监听器内处理该类别事件的逻辑, 例如:

//定义一个大的类型
public abstract class FirstType extends ApplicationEvent {

    public FirstType(Object source) {
        super(source);
    }
}

//基于该类型的第一种事件类型
    @Data
    public static class DogEvent extends FirstType{
        private String name;
        public DogEvent(Object source, String name) {
            super(source);
            this.name = name;
        }
    }

//基于该类型的第二种事件类型
    @Data
    public static class WolfEvent extends FirstType{
        private String name;
        public WolfEvent(Object source, String name) {
            super(source);
            this.name = name;
        }
    }

//监听器处理 这一大类型的事件  收到事件后 根据具体的事件类型做出相应的逻辑处理
    @EventListener
    public void first(FirstType firstType) {

        if (firstType instanceof CommonSpringEventType.DogEvent) {
            CommonSpringEventType.DogEvent dogEvent = (CommonSpringEventType.DogEvent) firstType;
            System.out.println("CommonSpringEventType.DogEvent " + dogEvent.getName());
        }
        if (firstType instanceof CommonSpringEventType.WolfEvent) {
            CommonSpringEventType.WolfEvent wolfEvent = (CommonSpringEventType.WolfEvent) firstType;
            System.out.println("CommonSpringEventType.WolfEvent " + wolfEvent.getName());
        }
    }

2.Spring事件异步化

2.1 注入线程池

//来自SimpleApplicationEventMulticaster中的源码
public void multicastEvent(final ApplicationEvent event, @Nullable ResolvableType eventType) {
		ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
		Executor executor = getTaskExecutor();
		for (ApplicationListener<?> listener : getApplicationListeners(event, type)) {
			if (executor != null) {
				executor.execute(() -> invokeListener(listener, event));
			}
			else {
				invokeListener(listener, event);
			}
		}
	}
由上述源码可以看出,在进行广播前会根据是否有线程池来判断是否走异步。如果这个广播的bean注入了线程池后就会走异步,如果没有就同步执行。

//我们可以自己定义一个bean来注入
   @Bean
    public Executor executor(){
     ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
     return executor;
    }

    @Bean
    public SimpleApplicationEventMulticaster applicationEventMulticaster(Executor executor) {
        SimpleApplicationEventMulticaster simpleApplicationEventMulticaster = new SimpleApplicationEventMulticaster();
        simpleApplicationEventMulticaster.setTaskExecutor(executor);
        return simpleApplicationEventMulticaster;
    }
//上述的applicationEventMulticaster这个bean 名字一定不能改,改了就不能生效,因为源码是根据beanName来找的,原因分析如下:
//这个是初始化applicationEventMulticaster的方法
protected void initApplicationEventMulticaster() {
		ConfigurableListableBeanFactory beanFactory = getBeanFactory();
        //此处是根据beanName在工厂中确认是否存在这样的bean,如果存在就会拿过来用
        //public static final String APPLICATION_EVENT_MULTICASTER_BEAN_NAME = "applicationEventMulticaster";
		if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
			this.applicationEventMulticaster =
					beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
			}
		}
		else {
            //如果不存在的话就new一个放到单例池中
			this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
			beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
		}
	}

注意:如果在上述的applicationEventMulticaster这个bean中配置了线程池会导致你的项目中的所有事件都是走异步的方式,所以你需要评估事件异步化的风险。因此当你想只针对某个事件使用异步的方式,就是我们下面所要讲的这个注解。

2.2@Async注解

使用@Async注解实行异步化和上述的异步化的逻辑是比较独立,上述是通过判断是否存在线程池,该注解是通过生成JDK动态代理类的方式实现的,因此该方法所对应的类必须存在接口。

//首先要在配置类或者是启动类上加上@EnableAsync注解
//使用也是很简单的就是在方法或者类上加上注解就可以实现异步了
@Component
public class SubscriberClass implements ApplicationListener<CurrentUser> {

    private static final Logger log = LoggerFactory.getLogger(SubscriberClass.class);
    @Override
    @Async
    public void onApplicationEvent(CurrentUser currentUser) {
        log.info("SubscriberClass + onApplicationEvent + " + currentUser.getName());
    }

}

虽然实现了异步,但是Async这个注解实现异步的线程池是有问题的。小编建议大家在使用这个注解时最好使用自己管理的线程池,spring官方给予的默认线程池是是SimpleAsyncTaskExecutor,该线程池默认来一个任务创建一个线程,若系统中不断的创建线程,最终会导致系统占用内存过高,引发OutOfMemoryError错误。当然你也可以设置属性进行限流操作,通常情况下会基于该注解的value值设置自己定义的线程池:

   @Bean
    public Executor executor(){
     ThreadPoolExecutor executor = new ThreadPoolExecutor(10, 20, 30, TimeUnit.SECONDS, new LinkedBlockingQueue<>(10));
     return executor;
    }

@Component
public class SubscriberClass implements ApplicationListener<CurrentUser> {

    private static final Logger log = LoggerFactory.getLogger(SubscriberClass.class);
    @Override
    @Async("executor") //此处设置对应的线程池bean名字
    public void onApplicationEvent(CurrentUser currentUser) {
        log.info("SubscriberClass + onApplicationEvent + " + currentUser.getName());
    }
}

3.Spring事件原理

先给大家看一个简单的应用:

public class MyEventDemo {

    interface MyApplicationEvent{}

    interface MyApplicationListener<E extends MyApplicationEvent>{
        void handleEvent(E e);
    }

    //定义事件主体
    @Data
    public static class EventPayload implements MyApplicationEvent{
        private String name;
        public EventPayload(String name){
            this.name = name;
        }
    }

    //定义第一个监听器
    public static class EventFirst implements MyApplicationListener<EventPayload>{

        @Override
        public void handleEvent(EventPayload eventPayload) {
            System.out.println("EventFirst + " + eventPayload.getName());
        }
    }
    //定义第二个监听器
    public static class EventSecond implements MyApplicationListener<EventPayload>{

        @Override
        public void handleEvent(EventPayload eventPayload) {
            System.out.println("EventSecond + " + eventPayload.getName());
        }
    }

    //模拟定义一个Spring的内置事件
    @Data
    public static class SpringInternalPayload implements MyApplicationEvent{
        private String path;
        public SpringInternalPayload(String path){
            this.path = path;
        }
    }

    //模拟定义一个Spring的内置的监听器
    public static class SpringInternalEvent implements MyApplicationListener{

        @Override
        public void handleEvent(MyApplicationEvent myApplicationEvent) {

            /**
             * 在Spring的内置事件中  你会发现会有这种类型判断的
             * 在事件中,一个event的接收器只能处理对应的event类型,如果你在接收事件的地方用某种具体的event类型接收
             * 如果别的类型发送来了会报错的,所以在Spring内置事件中会使用接口类型接收 然后做类型判断,不是处理该event的主体类型
             * 这个接收器就不会处理
             */
            if( myApplicationEvent instanceof SpringInternalPayload){
                SpringInternalPayload springInternalPayload = (SpringInternalPayload)myApplicationEvent;
                System.out.println("SpringInternalEvent + " + springInternalPayload.getPath());
            }
        }
    }
    public static void main(String[] args) {

        List<MyApplicationListener> listenerList = Arrays.asList(new EventFirst(), new EventSecond(), new SpringInternalEvent());
        EventPayload eventPayload = new EventPayload("Hello");

        for(MyApplicationListener listener : listenerList){
            listener.handleEvent(eventPayload);
        }
    }
}

实际上Spring就是基于上面的原理去实现,只不过Spring的内部实现还是很复杂的。大致步骤如下:

  1. 服务启动时通过实现该接口的类型加载所有监听器;
  2. 事件发送时根据发送的事件主体类型找到对应的监听器(有可能含有Spring内置的监听器);
  3. 对所有监听做执行操作,被执行的监听器如果不是该事件主体类型的监听器会有if类型判断进而不处理

4.总结

本文主要讲述了如何使用Spring的事件方式进行解耦,大致内容总结如下:

  • 事件的两种方式:实现接口或者使用@EventListener的注解。
  • 事件默认是同步:可以使用@Async注解和配置线程池的方式实现异步;
  • 使用异步的方式一些注意的事项,比如所有事件会异步/化,需要评估风险;
  • 通过一个简单的应用介绍了Spring事件的基本原理;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值