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,并不是简单比较类型。

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