亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

基于spring如何實現事件驅動實例代碼

發布時間:2020-09-04 22:10:50 來源:腳本之家 閱讀:243 作者:稀飯下雪 欄目:編程語言

干貨點

通過閱讀該篇博客,你可以了解了解java的反射機制、可以了解如何基于spring生命周期使用自定義注解解決日常研發問題。具體源碼可以點擊鏈接。

問題描述

在日常研發中,經常會遇見業務A的某個action被觸發后,同時觸發業務B的action的行為,這種單對單的形式可以直接在業務A的action執行結束后直接調用業務B的action,那么如果是單對多的情況呢?

方案解決

這里提供一種在日常研發中經常使用到的機制,基于spring實現的事件驅動,即在業務A的action執行完,拋出一個事件,而業務B、C、D等監聽到該事件后處理相應的業務。

場景范例

這里提供一個場景范例,該范例基于springboot空殼項目實現,具體可以查看源碼,此處只梳理關鍵步驟。

步驟一:

定義一個注解,標志接收事件的注解,即所有使用了該注解的函數都會在對應事件被拋出的時候被調用,該注解實現比較簡單,代碼如下

/**
 * @author xifanxiaxue
 * @date 3/31/19
 * @desc 接收事件的注解
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface ReceiveAnno {

 // 監聽的事件
 Class clz();
}

如果想了解注解多個參數的意義是什么的可以點擊鏈接查看博主之前寫過文章。

定義事件接口

/**
 * @author xifanxiaxue
 * @date 3/31/19
 * @desc
 */
public interface IEvent {
}

所有事件都需要實現該接口,主要是為了后面泛型和類型識別。

定義MethodInfo

/**
 * @author xifanxiaxue
 * @date 3/31/19
 * @desc
 */
public class MethodInfo {

 public Object obj;
 public Method method;

 public static MethodInfo valueOf(Method method, Object obj) {

 MethodInfo info = new MethodInfo();
 info.method = method;
 info.obj = obj;
 return info;
 }

 public Object getObj() {
 return obj;
 }

 public Method getMethod() {
 return method;
 }
}

該類只是做了Object和Method的封裝,沒有其他作用。

步驟二:

實現一個事件容器,該容器的作用是存放各個事件以及需要觸發的各個業務的method的對應關系。

/**
 * @author xifanxiaxue
 * @date 3/31/19
 * @desc 事件容器
 */
public class EventContainer {

 private static Map<Class<IEvent>, List<MethodInfo>> eventListMap = new HashMap<>();

 public static void addEventToMap(Class clz, Method method, Object obj) {

 List<MethodInfo> methodInfos = eventListMap.get(clz);
 if (methodInfos == null) {
 methodInfos = new ArrayList<>();
 eventListMap.put(clz, methodInfos);
 }

 methodInfos.add(MethodInfo.valueOf(method, obj));
 }

 public static void submit(Class clz) {

 List<MethodInfo> methodInfos = eventListMap.get(clz);
 if (methodInfos == null) {
 return;
 }

 for (MethodInfo methodInfo : methodInfos) {
 Method method = methodInfo.getMethod();
 try {
 method.setAccessible(true);
 method.invoke(methodInfo.getObj());
 } catch (IllegalAccessException e) {
 e.printStackTrace();
 } catch (InvocationTargetException e) {
 e.printStackTrace();
 }
 }
 }
}

其中的addEventToMap函數的作用是將對應的事件、事件觸發后需要觸發的對應業務內的Method存放在eventListMap內;而submit函數會在其他業務類內拋出事件的時候被調用,而作用是從eventListMap中取出對應的Method,并通過反射觸發。

步驟三:

實現事件處理器,該事件處理器的作用是在bean被spring容器實例化后去判斷對應的bean是否有相應函數加了@ReceiveAnno注解,如果有則從中取出對應的Event并放入EventContainer中。

/**
 * @author xifanxiaxue
 * @date 3/31/19
 * @desc 事件處理器
 */
@Component
public class EventProcessor extends InstantiationAwareBeanPostProcessorAdapter {

 @Override
 public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {

 ReflectionUtils.doWithLocalMethods(bean.getClass(), new ReflectionUtils.MethodCallback() {
 @Override
 public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {

 ReceiveAnno anno = method.getAnnotation(ReceiveAnno.class);
 if (anno == null) {
  return;
 }

 Class clz = anno.clz();
 try {
  if (!IEvent.class.isInstance(clz.newInstance())) {
  FormattingTuple message = MessageFormatter.format("{}沒有實現IEvent接口", clz);
  throw new RuntimeException(message.getMessage());
  }
 } catch (InstantiationException e) {
  e.printStackTrace();
 }

 EventContainer.addEventToMap(clz, method, bean);
 }
 });

 return super.postProcessAfterInstantiation(bean, beanName);
 }

}

關于InstantiationAwareBeanPostProcessorAdapter的描述,有需要的可以查看我之前的文章,其中比較詳細描述到Spring中的InstantiationAwareBeanPostProcessor類的作用。

步驟四:

對應的業務類的實現如下:

/**
 * @author xifanxiaxue
 * @date 3/31/19
 * @desc
 */
@Slf4j
@Service
public class AFuncService implements IAFuncService {

 @Override
 public void login() {
 log.info("[{}]拋出登錄事件 ... ", this.getClass());
 EventContainer.submit(LoginEvent.class);
 }
}

A業務類,login會在被調用的生活拋出LoginEvent事件。

/**
 * @author xifanxiaxue
 * @date 3/31/19
 * @desc
 */
@Service
@Slf4j
public class BFuncService implements IBFuncService {

 @ReceiveAnno(clz = LoginEvent.class)
 private void doAfterLogin() {
 log.info("[{}]監聽到登錄事件 ... ", this.getClass());
 }
}
/**
 * @author xifanxiaxue
 * @date 3/31/19
 * @desc
 */
@Service
@Slf4j
public class CFuncService implements ICFuncService {

 @ReceiveAnno(clz = LoginEvent.class)
 private void doAfterLogin() {
 log.info("[{}]監聽到登錄事件 ... ", this.getClass());
 }
}

B和C業務類的doAfterLogin都分別加了注解 @ReceiveAnno(clz = LoginEvent.class) ,在監聽到事件LoginEvent后被觸發。
為了觸發方便,我在spring提供的測試類內加了實現,代碼如下:

@RunWith(SpringRunner.class)
@SpringBootTest
public class EventMechanismApplicationTests {

 @Autowired
 private AFuncService aFuncService;

 @Test
 public void contextLoads() {
 aFuncService.login();
 }
}

可以從中看出啟動該測試類后,會調用業務A的login函數,而我們要的效果是B業務類和C業務類的doAfterLogin函數會被自動觸發,那么結果如何呢?

結果打印

基于spring如何實現事件驅動實例代碼

我們可以從結果打印中看到,在業務類A的login函數觸發后,業務類B和業務類C都監聽到了監聽到登錄事件,證明該機制正常解決了單對多的行為觸發問題。

總結

以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對億速云的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

札达县| 乌鲁木齐县| 精河县| 固阳县| 丰城市| 方正县| 芦溪县| 桐乡市| 双江| 平南县| 沈阳市| 墨玉县| 澜沧| 合肥市| 乃东县| 伊春市| 富宁县| 阳城县| 盐亭县| 贡嘎县| 丽江市| 康定县| 拉萨市| 柯坪县| 灵山县| 兰考县| 合肥市| 涞水县| 温泉县| 咸宁市| 虎林市| 邯郸县| 皮山县| 金乡县| 府谷县| 丁青县| 连云港市| 盖州市| 乡城县| 和林格尔县| 泸西县|