Spring事件监听机制

背景

当我们的数据发生变化时,有很多别的业务逻辑需要去做,那么很适合使用事件监听来解耦合。比如目前做过的一个接口,会去修改指令的状态,修改完之后,需要调用持仓、额度等接口,那么每次有新增逻辑都需要来改我的这个接口,这很不方便,我完全可以修改完了之后,直接发布一个事件,让别的模块来监听这个事件,来完成他们的逻辑。

使用

使用起来非常简单,基本上就是定义一个event、listener即可。

当发布事件时,会遍历所有的Listener,挑出符合当前发布事件的Listener,然后执行Listener中的逻辑。

编写的Listener需要注册到Spring容器中。

第一步,声明一个事件

public class IstrnDataStsEvent extends ApplicationEvent {

    /**
     * 数据, 简单起见只使用一个状态
     */
    private final String dataSts;

    public IstrnDataStsEvent(Object source, String dataSts) {
        super(source);
        this.dataSts = dataSts;
    }

    public String getDataSts() {
        return dataSts;
    }
}

第二步,监听这个事件,执行自己的逻辑

/**
 * 持仓
 */
@Slf4j
@Component
public class PositionIstrnDataStsListener implements ApplicationListener<IstrnDataStsEvent> {

    @Override
    public void onApplicationEvent(IstrnDataStsEvent event) {
        log.info("position exec logic, dataSts: {}", event.getDataSts());
    }
}
/**
 * 额度
 */
@Slf4j
@Component
public class QuotaIstrnDataStsListener implements ApplicationListener<IstrnDataStsEvent> {

    @Override
    public void onApplicationEvent(IstrnDataStsEvent event) {
        log.info("quota exec logic, dataSts: {}", event.getDataSts());
    }
}

或者使用@EventListener注解方式

@Slf4j
@Component
public class IstrnDataStsListener {

    /**
     * 通过方法参数来决定监听哪个事件
     * 方法参数个数只能是0或者1
     */
    @EventListener
    public void handlePostion(IstrnDataStsEvent event) {
        log.info("position exec logic, dataSts: {}", event.getDataSts());
    }

    /**
     * 通过注解中的classes参数来决定监听哪个事件
     * 使用此方式,如果方法里不需要获取参数时, 可以使用无参方法
     */
    @EventListener(classes = {IstrnDataStsEvent.class})
    public void handleQuota() {
        log.info("=========handleQuota========");
    }
}

第三步,发布该事件

@Service
public class IstrnDataStsChangeService implements ApplicationContextAware {

    private ApplicationContext ac;

    public void changeDataSts(String dataSts) {
        ac.publishEvent(new IstrnDataStsEvent(this, dataSts));
    }

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

异步执行

默认情况下,监听逻辑是同步顺序执行的,当然Spring也提供异步执行的方式。不过这需要自己注册一个ApplicationEventMulticaster,Spring就是使用该接口来注册监听以及发布事件的。

ApplicationEventMulticaster的创建在AbstractApplicationContext.refresh()方法中完成,里面会调用initApplicationEventMulticaster方法。

protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    // 从容器中获取
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        this.applicationEventMulticaster =
                beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
        if (logger.isTraceEnabled()) {
            logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
        }
    }
    else {
        // 容器中没有,则自己创建
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
        if (logger.isTraceEnabled()) {
            logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
                    "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
        }
    }
}

从初始方法可知,只需要我们自己创建一个SimpleApplicationEventMulticaster放到容器中,然后为它设置一个线程池。

@Configuration
public class EventConfig {

    /**
     * 注意bean的名称必须为这个
     */
    @Bean(name = AbstractApplicationContext.APPLICATION_EVENT_MULTICASTER_BEAN_NAME)
    public ApplicationEventMulticaster applicationEventMulticaster(BeanFactory beanFactory) {
        SimpleApplicationEventMulticaster eventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        // 设置线程池, 用于异步执行监听逻辑(线程池参数视情况而定)
        eventMulticaster.setTaskExecutor(Executors.newFixedThreadPool(10));
        return eventMulticaster;
    }
}

通用事件PayloadApplicationEvent

该类声明了一个泛型,因此可以存任意类型的数据,不过我认为还是自己创建一个专门的事件比较好,这里简单提下,是因为Spring可以通过泛型来区分事件。举个例子

/**
 * 监听PayloadApplicationEvent<Integer>
 */
public class IntegerListener implements ApplicationListener<PayloadApplicationEvent<Integer>> {
    
    @Override
    public void onApplicationEvent(PayloadApplicationEvent<Integer> event) {
        
    }
}
/**
 * 监听PayloadApplicationEvent<String>
 */
public class StringListener implements ApplicationListener<PayloadApplicationEvent<String>> {

    @Override
    public void onApplicationEvent(PayloadApplicationEvent<String> event) {

    }
}
// 发布事件
ac.publishEvent(new PayloadApplicationEvent<>(this, "string"));

在该例子中,虽然Java中由于泛型擦除PayloadApplicationEvent<Integer>PayloadApplicationEvent<String>是同一个类,但是只会触发StringListener。因为Spring内部会去解析Listener的ResolvableType以及Event的ResolvableType。然后判断Event的ResolvableType是否可以赋值给Listener的ResolvableType。另外PayloadApplicationEvent能够被解析出带有完整泛型的ResolvableType,是因为实现了ResolvableTypeProvider接口。

@Override
public ResolvableType getResolvableType() {
    // 根据payload的类型来解析出完整的带泛型的ResolvableType
    return ResolvableType.forClassWithGenerics(getClass(), this.payloadType);
}

对于ac.publishEvent(new PayloadApplicationEvent<>(this, "string"))

解析出来的ResolvableType=org.springframework.context.PayloadApplicationEvent<java.lang.String>

若没有实现ResolvableTypeProvider接口

则解析出来的ResolvableType=org.springframework.context.PayloadApplicationEvent<?>

IntegerListener的泛型参数ResolvableType=org.springframework.context.PayloadApplicationEvent<java.lang.Integer>

StringListener的泛型参数ResolvableType=org.springframework.context.PayloadApplicationEvent<java.lang.String>

此时ResolvableType=org.springframework.context.PayloadApplicationEvent<?>不能赋值给ResolvableType=org.springframework.context.PayloadApplicationEvent<java.lang.Integer>,也不能赋值给ResolvableType=org.springframework.context.PayloadApplicationEvent<java.lang.String>。也就是说没有任何监听器执行了。

如何根据ApplicationEvent去找对应的ApplicationListener

第一步:先解析ApplicationEventResolvableType

SimpleApplicationEventMulticaster.multicastEvent()

/**
 * 如果event有实现ResolvableTypeProvider接口,则调用ResolvableTypeProvider的getResolvableType方法
 * 因此如果特殊逻辑或者泛型需求, 事件可以实现该接口
 * 由于泛型擦除,泛型类是无法在运行时拿到具体的泛型类型的,因此可以实现ResolvableTypeProvider接口
 * 人为告诉Spring具体的泛型类型。
 * 参考: PayloadApplicationEvent
 */
ResolvableType.forInstance(event)

第二步:判断ApplicationListener是否支持ApplicationEventResolvableType

分为下面3种情况,该逻辑封装在GenericApplicationListenerAdapter.supportsEventType

  • 若为GenericApplicationListener接口,直接通过boolean supportsEventType(ResolvableType eventType)方法判断

  • 若为SmartApplicationListener接口,直接通过boolean supportsEventType(Class<? extends ApplicationEvent> eventType)判断

  • 普通的ApplicationListener接口,则解析ApplicationListener中泛型参数对应的ResolvableType,判断ApplicationEventResolvableType是否可以赋值给这个泛型参数的ResolvableType

/**
* 要解析出泛型参数,必须要有一个具体的类来实现拥有泛型参数的接口或类
* 通过Class.getGenericSuperclass或者getGenericInterfaces来拿到父类或者接口中的泛型类型
*/
public class IntegerListener implements ApplicationListener<Integer> {

   @Override
   public void onApplicationEvent(Integer event) {

   }
}
posted on 2023-08-07 19:34  wastonl  阅读(141)  评论(0)    收藏  举报