基于guava event的事件機(jī)制

我們知道有很多業(yè)務(wù)場(chǎng)景下,大量繁瑣的次流程需要處理,比如更新索引等操作。但是這樣一來(lái)業(yè)務(wù)主流程和次流程揉在一起顯得就沒(méi)那么干凈。如果一些比較重的東西可能使用mq來(lái)做比較好,但是我這里就講一下比較輕量級(jí)的做法。利用guava event來(lái)處理一些次流程。
首先我們先看下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);

我們一個(gè)個(gè)來(lái)看,EventBus其實(shí)是運(yùn)用了觀察者模式,我們往他注冊(cè)一個(gè)事件訂閱者,只要事件被投遞,投入的類型相匹配就會(huì)被訂閱者處理。如果是異步的事件

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);

現(xiàn)在我們把它和spring相結(jié)合,讓spring托管事件
EventBus注入spring容器

@Configuration
public class EventBusConfig {
    @Bean
    public EventBus eventBus(){
        return new EventBus();
    }

    @Bean
    public AsyncEventBus asyncEventBus(){
        return new AsyncEventBus(Global.executors);
    }
}

注冊(cè)帶有@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;
    }
}

統(tǒng)一抽象事件實(shí)體

@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);
        }

    }

}

如何使用?
業(yè)務(wù)方法上注解

    @GuavaEvent(value = "auth.login")
    String login(String username, String password);

實(shí)現(xiàn)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()就是方法的參數(shù)

github地址:https://github.com/www1350/Rick

實(shí)際運(yùn)用中還發(fā)現(xiàn)了有些需要被同步變量的異步事件,做了一個(gè)兼容

@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);
}

運(yùn)用

@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;
        }

    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 134,868評(píng)論 18 139
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,065評(píng)論 25 708
  • Spring Boot 參考指南 介紹 轉(zhuǎn)載自:https://www.gitbook.com/book/qbgb...
    毛宇鵬閱讀 46,941評(píng)論 6 342
  • 有大把的時(shí)間的時(shí)候覺(jué)得無(wú)事可做,也無(wú)做事的心境,心想,反正有大把時(shí)光~ 可是時(shí)間不夠用的時(shí)候,覺(jué)得時(shí)間寶貴,不珍惜...
    行云流水暢遨游閱讀 150評(píng)論 0 0
  • 由于Windows和Mac的編譯流程大同小異,如果需要查閱編譯流程請(qǐng)參考我的另一篇博文Mac OSX 10.12....
    Virson閱讀 1,501評(píng)論 0 1