真实的国产乱ⅩXXX66竹夫人,五月香六月婷婷激情综合,亚洲日本VA一区二区三区,亚洲精品一区二区三区麻豆

成都創(chuàng)新互聯(lián)網(wǎng)站制作重慶分公司

在Spring環(huán)境中事件驅(qū)動代碼的示例分析

在Spring環(huán)境中事件驅(qū)動代碼的示例分析,很多新手對此不是很清楚,為了幫助大家解決這個難題,下面小編將為大家詳細講解,有這方面需求的人可以來學習下,希望你能有所收獲。

目前創(chuàng)新互聯(lián)已為千余家的企業(yè)提供了網(wǎng)站建設(shè)、域名、網(wǎng)頁空間、網(wǎng)站運營、企業(yè)網(wǎng)站設(shè)計、剛察網(wǎng)站維護等服務(wù),公司將堅持客戶導向、應用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長,共同發(fā)展。

雖然現(xiàn)在的各種應用都是集群部署,單機部署的應用越來越少了,但是不可否認的是,市場上還是存在許多單機應用的。本文要介紹的是 Guava 中的 EventBus 的使用。

EventBus 處理的事情類似觀察者模式,基于事件驅(qū)動,觀察者們監(jiān)聽自己感興趣的特定事件,進行相應的處理。

在 Spring 環(huán)境中優(yōu)雅地使用 Guava 包中的 EventBus,對我們的代碼進行一定程度的解耦。

Step 0:添加 Guava 依賴 com.google.guava guava 22.0 作為 java 程序員,如果你還沒有使用過 Google Guava,請從現(xiàn)在開始將它加到你的每一個項目中。

Step 1:定義一個注解用于標記 listener /**

  • 用于標記 listener */ @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) public @interface EventBusListener { } Step 2:定義注冊中心 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,將他們注冊為監(jiān)聽者
    List listeners = SpringContextUtils.getBeansWithAnnotation(EventBusListener.class);
    for (Object listener : listeners) {
        asyncEventBus.register(listener);
        syncEventBus.register(listener);
    }
}

} Step 3:定義各種事件 舉個例子,我們定義一個訂單創(chuàng)建事件:

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 } Step 4:定義事件監(jiān)聽器 首先,類上面需要加我們之前定義的注解:@EventBusListener,然后監(jiān)聽方法需要加注解 @Subscribe,方法參數(shù)為具體事件。

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();
    // 訂單創(chuàng)建成功后的各種操作,如發(fā)短信、發(fā)郵件等等。
    // 注意,事件可以被訂閱多次,也就是說可以有很多方法監(jiān)聽 OrderCreatedEvent 事件,
    // 所以沒必要在一個方法中處理發(fā)短信、發(fā)郵件、更新庫存等
}

@Subscribe
public void change(OrderChangeEvent event) {
    // 處理訂單變化后的修改
    // 如發(fā)送提醒、更新物流等
}

} Step 5:發(fā)送事件 @Service public class OrderService {

@Autowired
private EventBusCenter eventBusCenter;

public void createOrder() {
    // 處理創(chuàng)建訂單
    // ...
    // 發(fā)送異步事件
    eventBusCenter.postAsync(new OrderCreatedEvent(1L, 1L));
}

} 總結(jié) EventBus 的好處在于,它將發(fā)生事件的代碼和事件處理的代碼進行了解耦。

比如系統(tǒng)中很多地方都會修改訂單,用戶可以自己修改、客服也可以修改、甚至可能是團購沒成團系統(tǒng)進行的訂單修改,所有這些觸發(fā)訂單修改的地方都要發(fā)短信、發(fā)郵件,假設(shè)以后還要增加其他操作,那么需要修改的地方就比較多。

而如果采用事件驅(qū)動的話,只要這些地方拋出事件就可以了,后續(xù)的維護是比較簡單的。

而且,EventBus 支持同步事件和異步事件,可以滿足我們不同場景下的需求。比如發(fā)短信,系統(tǒng)完全沒必要等在那邊,完全是可以異步做的。

附錄:SpringContextUtils 上面的代碼使用到了 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 getBean(String name) throws BeansException {
    return (T) beanFactory.getBean(name);
}

public static  T getBean(Class clz) throws BeansException {
    T result = beanFactory.getBean(clz);
    return result;
}

public static  List getBeansOfType(Class type) {
    return beanFactory.getBeansOfType(type).entrySet().stream().map(entry->entry.getValue()).collect(Collectors.toList());
}

// 上面的例子用到了這個
public static List getBeansWithAnnotation(Class annotationType) {
    Map beansWithAnnotation = beanFactory.getBeansWithAnnotation(annotationType);

    // java 8 的寫法,將 map 的 value 收集起來到一個 list 中
    return beansWithAnnotation.entrySet().stream().map(entry->entry.getValue()).collect(Collectors.toList());

    // java 7
    List result = new ArrayList<>();
    for (Map.Entry entry : beansWithAnnotation.entrySet()) {
        result.add(entry.getValue());
    }
    return result;
}

這次重新又用上了這個 eventbus,碰到一個新的問題:在使用同步事件的時候,怎樣將事件處理過程中拋出來的異常拋回給客戶端?

首先我們要明白,AsyncEventBus 是異步模式的,EventBus 是同步模式的,在使用同步模式的時候,線程 post 一個 event 以后,還是由當前線程來處理各個 Subscriber 中的操作的。

所以在調(diào)用 void eventbus.post(event) 這個方法后,線程會先去處理 Subscribers 中的操作,處理完了以后,post(event) 方法才會返回。

StackOverflow 上有很多人都碰到了這個問題,不過我沒有找到合適的解決方案,就自己造了一個。

解決方法很簡單,就是使用 ThreadLocal 來傳遞異常:

ThreadLocal threadLocal = new ThreadLocal();

/**

  • 管理同步事件 */ private EventBus syncEventBus = new EventBus(new SubscriberExceptionHandler() {

    @Override public void handleException(Throwable exception, SubscriberExceptionContext context) { if (exception instanceof ServiceException) { threadLocal.set((ServiceException) exception); } } });

public void postSync(Object event) { syncEventBus.post(event); ServiceException ex = threadLocal.get(); if (ex != null) { // 記得 remove threadLocal.remove(); throw ex; } } ps: 在多個 Subscriber 的場景中,在一個 Subscriber 中拋出異常,不會阻止線程執(zhí)行下一個 Subscriber 中的操作。在上面的代碼中,如果有多個 Subscriber 拋出異常,就是 threadLocal 會被設(shè)置多次,最終得到的是最后一個 ex 的值。

看完上述內(nèi)容是否對您有幫助呢?如果還想對相關(guān)知識有進一步的了解或閱讀更多相關(guān)文章,請關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝您對創(chuàng)新互聯(lián)的支持。


文章標題:在Spring環(huán)境中事件驅(qū)動代碼的示例分析
文章地址:http://weahome.cn/article/ihipgo.html

在線咨詢

微信咨詢

電話咨詢

028-86922220(工作日)

18980820575(7×24)

提交需求

返回頂部