原理参考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机制。借助两级消息通知实现廉价的同步方案。