雖然如今的各類應用都是集羣部署,單機部署的應用愈來愈少了,可是不能否認的是,市場上仍是存在許多單機應用的。本文要介紹的是 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;
}
}
複製代碼
(全文完)