Spring自动装配guava EventBus

原理参考ImportBeanDefinitionRegistrar+SPI简化Spring开发

guava EventBus是一个进程内事件总线,可以看做是消息队列的进程内版本,用作进程内解耦、通知、异步处理等。网上有很多对其介绍的。

com.alpha.coding.common.event这个package下是对guava EventBus的二次封装,方便在spring框架下使用,结合spring AOP机制,在切点拦截组装事件发布,通知对应的Listener处理。一个比较重要的概念是事件分类标识,每一个标识代表一类事件,用一个枚举类来描述子类。

public interface EventIdentifier {

    /**
     * 身份标识
     */
    Class<? extends EnumWithCodeSupplier> getIdentity();

}

如缓存更新事件:

@Getter
@AllArgsConstructor
public enum CacheEventType implements EnumWithCodeSupplier {

    DATA_A_CHANGE(1, "数据A更新"),
    ;

    private int type;
    private String desc;

    @Override
    public Supplier codeSupply() {
        return () -> this.type;
    }

}

定义好对应事件的Handler:

@Slf4j
@Component
public class DataAChangeEventHandler
        extends CallbackEventHandlerTemplate<Object, CacheEventType, CacheEventErrorType> {

    @Override
    public List<? extends AbstractEventHandleResult<Object, CacheEventErrorType>> handleWithStrategy(Set<Object> keys) {
        // TODO 执行业务逻辑,如刷新缓存
        return Lists.newArrayList();
    }

    @Override
    public CacheEventType getEventType() {
        return CacheEventType.DATA_A_CHANGE;
    }

}

以及切点加上事件注解:

@Slf4j
@Component
public class DataAService {

    @EventMonitor(eventType = @EventType(eventClass = CacheEventType.class,
            type = "DATA_A_CHANGE"), keyFrom = EventKeyFrom.REQUEST)
    public void changeDataAToDB(Object dataA) {

    }

}

在调用DataAService.changeDataAToDB()方法后就会触发异步事件通知到DataAChangeEventHandler处理。

下面是自动装配相关的定义:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@EnableAutoRegistrar
@Import(EventBusConfiguration.class)
@Repeatable(EnableAutoConfigEventBuss.class)
public @interface EnableAutoConfigEventBus {

    Class<? extends EnumWithCodeSupplier>[] eventIdentity() default {};

    boolean useDefaultBusInstance() default true;

    String eventBusInstanceName() default "";

}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@EnableAutoRegistrar
@Import(EventBusConfiguration.class)
public @interface EnableAutoConfigEventBuss {

    EnableAutoConfigEventBus[] value();
}

对应的SPI Handler

public class EnableAutoConfigEventBusHandler implements ConfigurationRegisterHandler {

    private static final AtomicInteger COUNT = new AtomicInteger(0);

    @Override
    public void registerBeanDefinitions(RegisterBeanDefinitionContext context) {
        Set<AnnotationAttributes> annotationAttributes = SpringAnnotationConfigUtils.attributesForRepeatable(
                context.getImportingClassMetadata(), EnableAutoConfigEventBuss.class, EnableAutoConfigEventBus.class);
        if (CollectionUtils.isEmpty(annotationAttributes)) {
            return;
        }
        final BeanDefinitionRegistry registry = context.getRegistry();
        for (AnnotationAttributes attribute : annotationAttributes) {
            final Class<?>[] identities = attribute.getClassArray("eventIdentity");
            final EventBus eventBus = getEventBusInstance(attribute, context.getBeanFactory());
            for (Class<?> identity : identities) {
                String beanName = EventConfiguration.class.getName() + "_AUTO_" + COUNT.incrementAndGet();
                BeanDefinitionBuilder beanDefinitionBuilder =
                        BeanDefinitionBuilder.genericBeanDefinition(EventConfiguration.class);
                beanDefinitionBuilder.addPropertyValue("identity", identity);
                beanDefinitionBuilder.addPropertyValue("eventBusInstance", eventBus);
                registry.registerBeanDefinition(beanName, beanDefinitionBuilder.getRawBeanDefinition());
            }
        }
    }

    private EventBus getEventBusInstance(AnnotationAttributes attribute, BeanFactory beanFactory) {
        if (attribute.getBoolean("useDefaultBusInstance")) {
            return (EventBus) beanFactory.getBean("defaultEventBusInstance");
        }
        final String eventBusInstanceName = attribute.getString("eventBusInstanceName");
        if (eventBusInstanceName.isEmpty()) {
            throw new NoSuchBeanDefinitionException("eventBusInstanceName is empty");
        }
        return (EventBus) beanFactory.getBean(eventBusInstanceName);
    }

    @Override
    public int getOrder() {
        return 0;
    }
}

几点说明:
1、@EnableAutoConfigEventBus注解上使用Import引入EventBusConfiguration,在此定义一些基本bean,以及扫描包下bean定义;
2、EnableAutoConfigEventBusHandler中自动装配,定义事件配置EventConfiguration,最终借助AsyncEventBusAutoConfig这个bean实现事件listener、handler等的自动组装

使用自动装配只需引入@EnableAutoConfigEventBus注解

@Configuration
@EnableAutoConfigEventBus(eventIdentity = CacheEventType.class)
public class EnableAutoConfigEventBusConfiguration {
}

举一个应用场景。管理端更新数据写入到数据库中,没有基于binlog这样的同步机制时,可采用上述进程内异步事件通知。对于多实例的分布式缓存以及本地缓存的失效与更新,可借助Redis Message机制。借助两级消息通知实现廉价的同步方案。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值