我們知道有很多業(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;
}
}
}