我们知道有很多业务场景下,大量繁琐的次流程需要处理,比如更新索引等操作。但是这样一来业务主流程和次流程揉在一起显得就没那么干净。如果一些比较重的东西可能使用mq来做比较好,但是我这里就讲一下比较轻量级的做法。利用guava event来处理一些次流程。
首先我们先看下guava event的最小demo
final EventBus eventBus = new EventBus();
eventBus.register(new Object(){
@Subscribe
public void lister(Integer integer) {
System.out.printf("%s from int%n", integer);
}
});
eventBus.post(1);
我们一个个来看,EventBus其实是运用了观察者模式,我们往他注册一个事件订阅者,只要事件被投递,投入的类型相匹配就会被订阅者处理。如果是异步的事件
ExecutorService executorService = Executors.newFixedThreadPool( 2 * Runtime.getRuntime().availableProcessors());
final AsyncEventBus asyncEventBus = new AsyncEventBus(executorService);
asyncEventBus.register(new Object(){
@Subscribe
public void listerLong(Long num) {
System.out.printf("%s from long%n", num);
}
});
asyncEventBus.post(1L);
现在我们把它和spring相结合,让spring托管事件
EventBus注入spring容器
@Configuration
public class EventBusConfig {
@Bean
public EventBus eventBus(){
return new EventBus();
}
@Bean
public AsyncEventBus asyncEventBus(){
return new AsyncEventBus(Global.executors);
}
}
注册带有@Subscribe的bean
@Component
public class EventPostProcessor implements BeanPostProcessor{
@Autowired
private EventBus eventBus;
@Autowired
private AsyncEventBus asyncEventBus;
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
Field[] fields = bean.getClass().getFields();
Method[] methods = bean.getClass().getDeclaredMethods();
if (methods == null || methods.length == 0) {
return bean;
}
for (Method method : methods){
Subscribe subscribe = method.getAnnotation(Subscribe.class);
if (subscribe == null) continue;
eventBus.register(bean);
asyncEventBus.register(bean);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Method[] methods = bean.getClass().getDeclaredMethods();
if (methods == null || methods.length == 0) {
return bean;
}
return bean;
}
}
统一抽象事件实体
@Data
public class Event {
private List<Object> data;
private String operator;
}
事件订阅者
public interface Handler {
@Subscribe
void handler(Event event);
}
自定义注解
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = {ElementType.METHOD,ElementType.TYPE})
public @interface GuavaEvent {
boolean enable() default true;
String value();
GuavaEventPostEnum advice() default GuavaEventPostEnum.AFTER;
boolean async() default true;
}
public enum GuavaEventPostEnum {
BEFORE,AFTER
}
自定义注解处理器
@Aspect
@Component
public class GuavaEventResolver {
@Autowired
private EventBus eventBus;
@Autowired
private AsyncEventBus asyncEventBus;
@Pointcut("@annotation(com.absurd.rick.annotation.GuavaEvent)")
public void execute(){}
@Before(value = "execute()")
public void handlerEventBefore(JoinPoint joinPoint){
MethodSignature ms = (MethodSignature) joinPoint.getSignature();
Method method = ms.getMethod();
GuavaEvent guavaEvent = method.getAnnotation(GuavaEvent.class);
if (guavaEvent == null) {
return;
}
if (!guavaEvent.enable()) return;
if (GuavaEventPostEnum.BEFORE.equals(guavaEvent.advice())){
postEvent(guavaEvent,joinPoint.getArgs());
}
}
@AfterReturning(value = "execute()")
public void handlerEventAfter(JoinPoint joinPoint){
MethodSignature ms = (MethodSignature) joinPoint.getSignature();
Method method = ms.getMethod();
GuavaEvent guavaEvent = method.getAnnotation(GuavaEvent.class);
if (guavaEvent == null) {
return;
}
if (!guavaEvent.enable()) return;
if (GuavaEventPostEnum.AFTER.equals(guavaEvent.advice())){
postEvent(guavaEvent,joinPoint.getArgs());
}
}
private void postEvent(GuavaEvent guavaEvent, Object[] args) {
Event event = new Event();
event.setOperator(guavaEvent.value());
List<Object> data = new ArrayList<Object>();
if (args == null || args.length == 0) {
event.setData(data);
}
for (Object arg : args) {
data.add(arg);
}
event.setData(data);
event.setOperator(guavaEvent.value());
if (guavaEvent.async()){
asyncEventBus.post(event);
}else{
eventBus.post(event);
}
}
}
如何使用?
业务方法上注解
@GuavaEvent(value = "auth.login")
String login(String username, String password);
实现Handler
@Slf4j
@Component
public class EventHandler implements Handler{
@Subscribe
@Override
public void handler(Event event) {
List<Object> args = event.getData();
String operator = event.getOperator();
switch (operator){
case "auth.login":
log.info("{},{}",args.get(0),args.get(1));
break;
case "car.get":
log.info("{}",args.get(0));
break;
default:
break;
}
}
}
event.getData()就是方法的参数
github地址:https://github.com/www1350/Rick
实际运用中还发现了有些需要被同步变量的异步事件,做了一个兼容
@Data
public class Event {
private List<Object> data;
private String operator;
private Map<String,Object> extraData;
}
public interface EventSyncExtra {
Object getExtra();
void setExtra(Object extra);
}
GuavaEventResolver的postEvent
Collection<Object> eventSyncs = SpringContextUtil.getBeanByType(EventSyncExtra.class);
for(Object eventSync : eventSyncs){
if (eventSync instanceof EventSyncExtra){
extraMap.put(eventSync.getClass().getName(),((EventSyncExtra)eventSync).getExtra());
}
}
public interface Handler {
default void initExtra(Event event){
Map<String,Object> map = event.getExtraData();
Collection<Object> eventSyncs = SpringContextUtil.getBeanByType(EventSyncExtra.class);
for(Object eventSync : eventSyncs){
if (eventSync instanceof EventSyncExtra){
EventSyncExtra eventSyncExtra = (EventSyncExtra) eventSync;
eventSyncExtra.setExtra(map.get(eventSync.getClass().getName()));
}
}
}
@Subscribe
void handler(Event event);
}
运用
@Component
public class AuthEventSyncExtra implements EventSyncExtra{
@Override
public Object getExtra() {
return AuthHolder.getThreadMap();
}
@Override
public void setExtra(Object extra) {
AuthHolder.set((Map<String, Object>)extra);
}
}
@Slf4j
@Component
public class EventHandler implements Handler{
@Subscribe
@Override
public void handler(Event event) {
initExtra(event);
List<Object> args = event.getData();
String operator = event.getOperator();
switch (operator){
case "auth.login":
log.info("{},{}",args.get(0),args.get(1));
break;
case "car.get":
log.info("{}",args.get(0));
log.info(AuthHolder.username());
break;
default:
break;
}
}
}