虽然如今的各类应用都是集群部署,单机部署的应用愈来愈少了,可是不能否认的是,市场上仍是存在许多单机应用的。本文要介绍的是 Guava 中的 EventBus 的使用。java
EventBus 处理的事情相似观察者模式,基于事件驱动,观察者们监听本身感兴趣的特定事件,进行相应的处理。程序员
本文想要介绍的内容是,在 Spring 环境中优雅地使用 Guava 包中的 EventBus,对咱们的代码进行必定程度的解耦。固然,本文不介绍 EventBus 的原理,我所说的优雅也只是我以为优雅,也许读者有更漂亮的代码,欢迎在评论区留言。spring
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>22.0</version>
</dependency>
复制代码
做为 java 程序员,若是你尚未使用过 Google Guava,请从如今开始将它加到你的每个项目中。bash
/**
* 用于标记 listener
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBusListener {
}
复制代码
package com.javadoop.eventbus;
import java.util.List;
import java.util.concurrent.Executors;
import javax.annotation.PostConstruct;
import org.springframework.stereotype.Component;
import com.google.common.eventbus.AsyncEventBus;
import com.google.common.eventbus.EventBus;
import com.hongjiev.javadoop.util.SpringContextUtils;
@Component
public class EventBusCenter {
// 管理同步事件
private EventBus syncEventBus = new EventBus();
// 管理异步事件
private AsyncEventBus asyncEventBus = new AsyncEventBus(Executors.newCachedThreadPool());
public void postSync(Object event) {
syncEventBus.post(event);
}
public void postAsync(Object event) {
asyncEventBus.post(event);
}
@PostConstruct
public void init() {
// 获取全部带有 @EventBusListener 的 bean,将他们注册为监听者
List<Object> listeners = SpringContextUtils.getBeansWithAnnotation(EventBusListener.class);
for (Object listener : listeners) {
asyncEventBus.register(listener);
syncEventBus.register(listener);
}
}
}
复制代码
举个例子,咱们定义一个订单建立事件:异步
package com.javadoop.eventbus.event;
public class OrderCreatedEvent {
private long orderId;
private long userId;
public OrderCreatedEvent(long orderId, long userId) {
this.setOrderId(orderId);
this.setUserId(userId);
}
// getter、setter
}
复制代码
首先,类上面须要加咱们以前定义的注解:@EventBusListener,而后监听方法须要加注解 @Subscribe,方法参数为具体事件。async
package com.javadoop.eventbus.listener;
import org.springframework.stereotype.Component;
import com.google.common.eventbus.Subscribe;
import com.javadoop.eventbus.EventBusListener;
import com.javadoop.eventbus.event.OrderCreatedEvent;
@Component
@EventBusListener
public class OrderChangeListener {
@Subscribe
public void created(OrderCreatedEvent event) {
long orderId = event.getOrderId();
long userId = event.getUserId();
// 订单建立成功后的各类操做,如发短信、发邮件等等。
// 注意,事件能够被订阅屡次,也就是说能够有不少方法监听 OrderCreatedEvent 事件,
// 因此不必在一个方法中处理发短信、发邮件、更新库存等
}
@Subscribe
public void change(OrderChangeEvent event) {
// 处理订单变化后的修改
// 如发送提醒、更新物流等
}
}
复制代码
@Service
public class OrderService {
@Autowired
private EventBusCenter eventBusCenter;
public void createOrder() {
// 处理建立订单
// ...
// 发送异步事件
eventBusCenter.postAsync(new OrderCreatedEvent(1L, 1L));
}
}
复制代码
EventBus 的好处在于,它将发生事件的代码和事件处理的代码进行了解耦。ide
好比系统中不少地方都会修改订单,用户能够本身修改、客服也能够修改、甚至多是团购没成团系统进行的订单修改,全部这些触发订单修改的地方都要发短信、发邮件,假设之后还要增长其余操做,那么须要修改的地方就比较多。工具
而若是采用事件驱动的话,只要这些地方抛出事件就能够了,后续的维护是比较简单的。oop
并且,EventBus 支持同步事件和异步事件,能够知足咱们不一样场景下的需求。好比发短信,系统彻底不必等在那边,彻底是能够异步作的。post
上面的代码使用到了 SpringContextUtils,我想大部分的 Spring 应用都会写这么一个工具类来从 Spring 容器中获取 Bean,用于一些不方便采用注入的地方。
@Component
public class SpringContextUtils implements BeanFactoryPostProcessor {
private static ConfigurableListableBeanFactory beanFactory;
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
SpringContextUtils.beanFactory = configurableListableBeanFactory;
}
public static <T> T getBean(String name) throws BeansException {
return (T) beanFactory.getBean(name);
}
public static <T> T getBean(Class<T> clz) throws BeansException {
T result = beanFactory.getBean(clz);
return result;
}
public static <T> List<T> getBeansOfType(Class<T> type) {
return beanFactory.getBeansOfType(type).entrySet().stream().map(entry->entry.getValue()).collect(Collectors.toList());
}
// 上面的例子用到了这个
public static List<Object> getBeansWithAnnotation(Class<? extends Annotation> annotationType) {
Map<String, Object> beansWithAnnotation = beanFactory.getBeansWithAnnotation(annotationType);
// java 8 的写法,将 map 的 value 收集起来到一个 list 中
return beansWithAnnotation.entrySet().stream().map(entry->entry.getValue()).collect(Collectors.toList());
// java 7
List<Object> result = new ArrayList<>();
for (Map.Entry<String, Object> entry : beansWithAnnotation.entrySet()) {
result.add(entry.getValue());
}
return result;
}
}
复制代码
(全文完)