摘要:實體代碼如下所示恒宇少年碼云用戶名密碼創(chuàng)建內添加一個注冊方法,該方法只是實現(xiàn)注冊事件發(fā)布功能,代碼如下所示恒宇少年碼云用戶注冊方法省略其他邏輯發(fā)布事件事件發(fā)布是由對象管控的,我們發(fā)布事件前需要注入對象調用方法完成事件發(fā)布。
ApplicationEvent以及Listener是Spring為我們提供的一個事件監(jiān)聽、訂閱的實現(xiàn),內部實現(xiàn)原理是觀察者設計模式,設計初衷也是為了系統(tǒng)業(yè)務邏輯之間的解耦,提高可擴展性以及可維護性。事件發(fā)布者并不需要考慮誰去監(jiān)聽,監(jiān)聽具體的實現(xiàn)內容是什么,發(fā)布者的工作只是為了發(fā)布事件而已。
本章目標我們平時日常生活中也是經(jīng)常會有這種情況存在,如:我們在平時拔河比賽中,裁判員給我們吹響了開始的信號,也就是給我們發(fā)布了一個開始的事件,而拔河雙方人員都在監(jiān)聽著這個事件,一旦事件發(fā)布后雙方人員就開始往自己方使勁。而裁判并不關心你比賽的過程,只是給你發(fā)布事件你執(zhí)行就可以了。
我們本章在SpringBoot平臺上通過ApplicationEvents以及Listener來完成簡單的注冊事件流程。
構建項目我們本章只是簡單的講解如何使用ApplicationEvent以及Listener來完成業(yè)務邏輯的解耦,不涉及到數(shù)據(jù)交互所以依賴需要引入的也比較少,項目pom.xml配置文件如下所示:
.....//省略.....//省略 org.springframework.boot spring-boot-starter-web org.projectlombok lombok 1.16.16 org.springframework.boot spring-boot-starter-test test
其中l(wèi)ombok依賴大家有興趣可以去深研究下,這是一個很好的工具,它可以結合Idea開發(fā)工具完成對實體的動態(tài)添加構造函數(shù)、Getter/Setter方法、toString方法等。
創(chuàng)建UserRegisterEvent事件我們先來創(chuàng)建一個事件,監(jiān)聽都是圍繞著事件來掛起的。事件代碼如下所示:
package com.yuqiyu.chapter27.event; import com.yuqiyu.chapter27.bean.UserBean; import lombok.Getter; import org.springframework.context.ApplicationEvent; /** * ======================== * Created with IntelliJ IDEA. * User:恒宇少年 * Date:2017/7/21 * Time:10:08 * 碼云:http://git.oschina.net/jnyqy * ======================== */ @Getter public class UserRegisterEvent extends ApplicationEvent { //注冊用戶對象 private UserBean user; /** * 重寫構造函數(shù) * @param source 發(fā)生事件的對象 * @param user 注冊用戶對象 */ public UserRegisterEvent(Object source,UserBean user) { super(source); this.user = user; } }
我們自定義事件UserRegisterEvent繼承了ApplicationEvent,繼承后必須重載構造函數(shù),構造函數(shù)的參數(shù)可以任意指定,其中source參數(shù)指的是發(fā)生事件的對象,一般我們在發(fā)布事件時使用的是this關鍵字代替本類對象,而user參數(shù)是我們自定義的注冊用戶對象,該對象可以在監(jiān)聽內被獲取。
創(chuàng)建UserBean在Spring內部中有多種方式實現(xiàn)監(jiān)聽如:@EventListener注解、實現(xiàn)ApplicationListener泛型接口、實現(xiàn)SmartApplicationListener接口等,我們下面來講解下這三種方式分別如何實現(xiàn)。
我們簡單創(chuàng)建一個用戶實體,并添加兩個字段:用戶名、密碼。實體代碼如下所示:
package com.yuqiyu.chapter27.bean; import lombok.Data; /** * ======================== * Created with IntelliJ IDEA. * User:恒宇少年 * Date:2017/7/21 * Time:10:05 * 碼云:http://git.oschina.net/jnyqy * ======================== */ @Data public class UserBean { //用戶名 private String name; //密碼 private String password; }創(chuàng)建UserService
UserService內添加一個注冊方法,該方法只是實現(xiàn)注冊事件發(fā)布功能,代碼如下所示:
package com.yuqiyu.chapter27.service; import com.yuqiyu.chapter27.bean.UserBean; import com.yuqiyu.chapter27.event.UserRegisterEvent; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Service; /** * ======================== * Created with IntelliJ IDEA. * User:恒宇少年 * Date:2017/7/21 * Time:10:11 * 碼云:http://git.oschina.net/jnyqy * ======================== */ @Service public class UserService { @Autowired ApplicationContext applicationContext; /** * 用戶注冊方法 * @param user */ public void register(UserBean user) { //../省略其他邏輯 //發(fā)布UserRegisterEvent事件 applicationContext.publishEvent(new UserRegisterEvent(this,user)); } }
事件發(fā)布是由ApplicationContext對象管控的,我們發(fā)布事件前需要注入ApplicationContext對象調用publishEvent方法完成事件發(fā)布。
創(chuàng)建UserController創(chuàng)建一個@RestController控制器,對應添加一個注冊方法簡單實現(xiàn),代碼如下所示:
package com.yuqiyu.chapter27.controller; import com.yuqiyu.chapter27.bean.UserBean; import com.yuqiyu.chapter27.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * 用戶控制器 * ======================== * Created with IntelliJ IDEA. * User:恒宇少年 * Date:2017/7/21 * Time:10:05 * 碼云:http://git.oschina.net/jnyqy * ======================== */ @RestController public class UserController { //用戶業(yè)務邏輯實現(xiàn) @Autowired private UserService userService; /** * 注冊控制方法 * @param user 用戶對象 * @return */ @RequestMapping(value = "/register") public String register ( UserBean user ) { //調用注冊業(yè)務邏輯 userService.register(user); return "注冊成功."; } }@EventListener實現(xiàn)監(jiān)聽
注解方式比較簡單,并不需要實現(xiàn)任何接口,具體代碼實現(xiàn)如下所示:
package com.yuqiyu.chapter27.listener; import com.yuqiyu.chapter27.bean.UserBean; import com.yuqiyu.chapter27.event.UserRegisterEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; /** * 使用@EventListener方法實現(xiàn)注冊事件監(jiān)聽 * ======================== * Created with IntelliJ IDEA. * User:恒宇少年 * Date:2017/7/21 * Time:10:50 * 碼云:http://git.oschina.net/jnyqy * ======================== */ @Component public class AnnotationRegisterListener { /** * 注冊監(jiān)聽實現(xiàn)方法 * @param userRegisterEvent 用戶注冊事件 */ @EventListener public void register(UserRegisterEvent userRegisterEvent) { //獲取注冊用戶對象 UserBean user = userRegisterEvent.getUser(); //../省略邏輯 //輸出注冊用戶信息 System.out.println("@EventListener注冊信息,用戶名:"+user.getName()+",密碼:"+user.getPassword()); } }
我們只需要讓我們的監(jiān)聽類被Spring所管理即可,在我們用戶注冊監(jiān)聽實現(xiàn)方法上添加@EventListener注解,該注解會根據(jù)方法內配置的事件完成監(jiān)聽。下面我們啟動項目來測試下我們事件發(fā)布時是否被監(jiān)聽者所感知。
測試事件監(jiān)聽使用SpringBootApplication方式啟動成功后,我們來訪問下地址:http://127.0.0.1:8080/register?name=admin&password=123456,界面輸出內容肯定是“注冊成功”,這個是沒有問題的,我們直接查看控制臺輸出內容,如下所示:
2017-07-21 11:09:52.532 INFO 10460 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet "dispatcherServlet" 2017-07-21 11:09:52.532 INFO 10460 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet "dispatcherServlet": initialization started 2017-07-21 11:09:52.545 INFO 10460 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet "dispatcherServlet": initialization completed in 13 ms @EventListener注冊信息,用戶名:admin,密碼:123456
可以看到我們使用@EventListener注解配置的監(jiān)聽已經(jīng)生效了,當我們在UserService內發(fā)布了注冊事件時,監(jiān)聽方法自動被調用并且輸出內信息到控制臺。
ApplicationListener實現(xiàn)監(jiān)聽這種方式也是Spring之前比較常用的監(jiān)聽事件方式,在實現(xiàn)ApplicationListener接口時需要將監(jiān)聽事件作為泛型傳遞,監(jiān)聽實現(xiàn)代碼如下所示:
package com.yuqiyu.chapter27.listener; import com.yuqiyu.chapter27.bean.UserBean; import com.yuqiyu.chapter27.event.UserRegisterEvent; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; /** * 原始方式實現(xiàn) * 用戶注冊監(jiān)聽 * ======================== * Created with IntelliJ IDEA. * User:恒宇少年 * Date:2017/7/21 * Time:10:24 * 碼云:http://git.oschina.net/jnyqy * ======================== */ @Component public class RegisterListener implements ApplicationListener{ /** * 實現(xiàn)監(jiān)聽 * @param userRegisterEvent */ @Override public void onApplicationEvent(UserRegisterEvent userRegisterEvent) { //獲取注冊用戶對象 UserBean user = userRegisterEvent.getUser(); //../省略邏輯 //輸出注冊用戶信息 System.out.println("注冊信息,用戶名:"+user.getName()+",密碼:"+user.getPassword()); } }
我們實現(xiàn)接口后需要使用@Component注解來聲明該監(jiān)聽需要被Spring注入管理,當有UserRegisterEvent事件發(fā)布時監(jiān)聽程序會自動調用onApplicationEvent方法并且將UserRegisterEvent對象作為參數(shù)傳遞。
我們UserService內的發(fā)布事件不需要修改,我們重啟下項目再次訪問之前的地址查看控制臺輸出的內容如下所示:
2017-07-21 13:03:35.399 INFO 4324 --- [nio-8080-exec-2] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet "dispatcherServlet" 2017-07-21 13:03:35.399 INFO 4324 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : FrameworkServlet "dispatcherServlet": initialization started 2017-07-21 13:03:35.411 INFO 4324 --- [nio-8080-exec-2] o.s.web.servlet.DispatcherServlet : FrameworkServlet "dispatcherServlet": initialization completed in 12 ms 注冊信息,用戶名:admin,密碼:123456
我們看到了控制臺打印了我們監(jiān)聽內輸出用戶信息,事件發(fā)布后就不會考慮具體哪個監(jiān)聽去處理業(yè)務,甚至可以存在多個監(jiān)聽同時需要處理業(yè)務邏輯。
郵件通知監(jiān)聽我們在注冊時如果不僅僅是記錄注冊信息到數(shù)據(jù)庫,還需要發(fā)送郵件通知用戶,當然我們可以創(chuàng)建多個監(jiān)聽同時監(jiān)聽UserRegisterEvent事件,接下來我們先來實現(xiàn)這個需求。
我們使用注解的方式來完成郵件發(fā)送監(jiān)聽實現(xiàn),代碼如下所示:
package com.yuqiyu.chapter27.listener; import com.yuqiyu.chapter27.event.UserRegisterEvent; import org.springframework.context.event.EventListener; import org.springframework.stereotype.Component; /** * 注冊用戶事件發(fā)送郵件監(jiān)聽 * ======================== * Created with IntelliJ IDEA. * User:恒宇少年 * Date:2017/7/21 * Time:13:08 * 碼云:http://git.oschina.net/jnyqy * ======================== */ @Component public class RegisterUserEmailListener { /** * 發(fā)送郵件監(jiān)聽實現(xiàn) * @param userRegisterEvent 用戶注冊事件 */ @EventListener public void sendMail(UserRegisterEvent userRegisterEvent) { System.out.println("用戶注冊成功,發(fā)送郵件。"); } }
監(jiān)聽編寫完成后,我們重啟項目,再次訪問注冊請求地址查看控制臺輸出內容如下所示:
2017-07-21 13:09:20.671 INFO 7808 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet "dispatcherServlet" 2017-07-21 13:09:20.671 INFO 7808 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet "dispatcherServlet": initialization started 2017-07-21 13:09:20.685 INFO 7808 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet "dispatcherServlet": initialization completed in 14 ms 用戶注冊成功,發(fā)送郵件。 注冊信息,用戶名:admin,密碼:123456
我們看到控制臺輸出的內容感到比較疑惑,我注冊時用戶信息寫入數(shù)據(jù)庫應該在發(fā)送郵件前面,為什么沒有在第一步執(zhí)行呢?
好了,證明了一點,事件監(jiān)聽是無序的,監(jiān)聽到的事件先后順序完全隨機出現(xiàn)的。我們接下來使用SmartApplicationListener實現(xiàn)監(jiān)聽方式來實現(xiàn)該邏輯。
我們對注冊用戶以及發(fā)送郵件的監(jiān)聽重新編寫,注冊用戶寫入數(shù)據(jù)庫監(jiān)聽代碼如下所示:
package com.yuqiyu.chapter27.listener; import com.yuqiyu.chapter27.bean.UserBean; import com.yuqiyu.chapter27.event.UserRegisterEvent; import com.yuqiyu.chapter27.service.UserService; import org.springframework.context.ApplicationEvent; import org.springframework.context.event.SmartApplicationListener; import org.springframework.stereotype.Component; /** * 用戶注冊>>>保存用戶信息監(jiān)聽 * ======================== * Created with IntelliJ IDEA. * User:恒宇少年 * Date:2017/7/21 * Time:10:09 * 碼云:http://git.oschina.net/jnyqy * ======================== */ @Component public class UserRegisterListener implements SmartApplicationListener { /** * 該方法返回true&supportsSourceType同樣返回true時,才會調用該監(jiān)聽內的onApplicationEvent方法 * @param aClass 接收到的監(jiān)聽事件類型 * @return */ @Override public boolean supportsEventType(Class extends ApplicationEvent> aClass) { //只有UserRegisterEvent監(jiān)聽類型才會執(zhí)行下面邏輯 return aClass == UserRegisterEvent.class; } /** * 該方法返回true&supportsEventType同樣返回true時,才會調用該監(jiān)聽內的onApplicationEvent方法 * @param aClass * @return */ @Override public boolean supportsSourceType(Class> aClass) { //只有在UserService內發(fā)布的UserRegisterEvent事件時才會執(zhí)行下面邏輯 return aClass == UserService.class; } /** * supportsEventType & supportsSourceType 兩個方法返回true時調用該方法執(zhí)行業(yè)務邏輯 * @param applicationEvent 具體監(jiān)聽實例,這里是UserRegisterEvent */ @Override public void onApplicationEvent(ApplicationEvent applicationEvent) { //轉換事件類型 UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent; //獲取注冊用戶對象信息 UserBean user = userRegisterEvent.getUser(); //.../完成注冊業(yè)務邏輯 System.out.println("注冊信息,用戶名:"+user.getName()+",密碼:"+user.getPassword()); } /** * 同步情況下監(jiān)聽執(zhí)行的順序 * @return */ @Override public int getOrder() { return 0; } }
SmartApplicationListener接口繼承了全局監(jiān)聽ApplicationListener,并且泛型對象使用的ApplicationEvent來作為全局監(jiān)聽,可以理解為使用SmartApplicationListener作為監(jiān)聽父接口的實現(xiàn),監(jiān)聽所有事件發(fā)布。
既然是監(jiān)聽所有的事件發(fā)布,那么SmartApplicationListener接口添加了兩個方法supportsEventType、supportsSourceType來作為區(qū)分是否是我們監(jiān)聽的事件,只有這兩個方法同時返回true時才會執(zhí)行onApplicationEvent方法。
可以看到除了上面的方法,還提供了一個getOrder方法,這個方法就可以解決執(zhí)行監(jiān)聽的順序問題,return的數(shù)值越小證明優(yōu)先級越高,執(zhí)行順序越靠前。
注冊成功發(fā)送郵件通知監(jiān)聽代碼如下所示:
package com.yuqiyu.chapter27.listener.order; import com.yuqiyu.chapter27.bean.UserBean; import com.yuqiyu.chapter27.event.UserRegisterEvent; import com.yuqiyu.chapter27.service.UserService; import org.springframework.context.ApplicationEvent; import org.springframework.context.event.SmartApplicationListener; import org.springframework.stereotype.Component; /** * ======================== * Created with IntelliJ IDEA. * User:恒宇少年 * Date:2017/7/21 * Time:13:38 * 碼云:http://git.oschina.net/jnyqy * ======================== */ @Component public class UserRegisterSendMailListener implements SmartApplicationListener { /** * 該方法返回true&supportsSourceType同樣返回true時,才會調用該監(jiān)聽內的onApplicationEvent方法 * @param aClass 接收到的監(jiān)聽事件類型 * @return */ @Override public boolean supportsEventType(Class extends ApplicationEvent> aClass) { //只有UserRegisterEvent監(jiān)聽類型才會執(zhí)行下面邏輯 return aClass == UserRegisterEvent.class; } /** * 該方法返回true&supportsEventType同樣返回true時,才會調用該監(jiān)聽內的onApplicationEvent方法 * @param aClass * @return */ @Override public boolean supportsSourceType(Class> aClass) { //只有在UserService內發(fā)布的UserRegisterEvent事件時才會執(zhí)行下面邏輯 return aClass == UserService.class; } /** * supportsEventType & supportsSourceType 兩個方法返回true時調用該方法執(zhí)行業(yè)務邏輯 * @param applicationEvent 具體監(jiān)聽實例,這里是UserRegisterEvent */ @Override public void onApplicationEvent(ApplicationEvent applicationEvent) { //轉換事件類型 UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent; //獲取注冊用戶對象信息 UserBean user = userRegisterEvent.getUser(); System.out.println("用戶:"+user.getName()+",注冊成功,發(fā)送郵件通知。"); } /** * 同步情況下監(jiān)聽執(zhí)行的順序 * @return */ @Override public int getOrder() { return 1; } }
在getOrder方法內我們返回的數(shù)值為“1”,這就證明了需要在保存注冊用戶信息監(jiān)聽后執(zhí)行,下面我們重啟項目訪問注冊地址查看控制臺輸出內容如下所示:
2017-07-21 13:40:43.104 INFO 10128 --- [nio-8080-exec-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring FrameworkServlet "dispatcherServlet" 2017-07-21 13:40:43.104 INFO 10128 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet "dispatcherServlet": initialization started 2017-07-21 13:40:43.119 INFO 10128 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet : FrameworkServlet "dispatcherServlet": initialization completed in 15 ms 注冊信息,用戶名:admin,密碼:123456 用戶:admin,注冊成功,發(fā)送郵件通知。
這次我們看到了輸出的順序就是正確的了,先保存信息然后再發(fā)送郵件通知。
使用@Async實現(xiàn)異步監(jiān)聽如果說我們不希望在執(zhí)行監(jiān)聽時等待監(jiān)聽業(yè)務邏輯耗時,發(fā)布監(jiān)聽后立即要對接口或者界面做出反映,我們該怎么做呢?
@Aysnc其實是Spring內的一個組件,可以完成對類內單個或者多個方法實現(xiàn)異步調用,這樣可以大大的節(jié)省等待耗時。內部實現(xiàn)機制是線程池任務ThreadPoolTaskExecutor,通過線程池來對配置@Async的方法或者類做出執(zhí)行動作。
線程任務池配置我們創(chuàng)建一個ListenerAsyncConfiguration,并且使用@EnableAsync注解開啟支持異步處理,具體代碼如下所示:
package com.yuqiyu.chapter27; import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler; import org.springframework.context.annotation.Configuration; import org.springframework.scheduling.annotation.AsyncConfigurer; import org.springframework.scheduling.annotation.EnableAsync; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import java.util.concurrent.Executor; /** * 異步監(jiān)聽配置 * ======================== * Created with IntelliJ IDEA. * User:恒宇少年 * Date:2017/7/21 * Time:14:04 * 碼云:http://git.oschina.net/jnyqy * ======================== */ @Configuration @EnableAsync public class ListenerAsyncConfiguration implements AsyncConfigurer { /** * 獲取異步線程池執(zhí)行對象 * @return */ @Override public Executor getAsyncExecutor() { //使用Spring內置線程池任務對象 ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor(); //設置線程池參數(shù) taskExecutor.setCorePoolSize(5); taskExecutor.setMaxPoolSize(10); taskExecutor.setQueueCapacity(25); taskExecutor.initialize(); return taskExecutor; } @Override public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() { return null; } }
我們自定義的監(jiān)聽異步配置類實現(xiàn)了AsyncConfigurer接口并且實現(xiàn)內getAsyncExecutor方法以提供線程任務池對象的獲取。
我們只需要在異步方法上添加@Async注解就可以實現(xiàn)方法的異步調用,為了證明這一點,我們在發(fā)送郵件onApplicationEvent方法內添加線程阻塞3秒,修改后的代碼如下所示:
/** * supportsEventType & supportsSourceType 兩個方法返回true時調用該方法執(zhí)行業(yè)務邏輯 * @param applicationEvent 具體監(jiān)聽實例,這里是UserRegisterEvent */ @Override @Async public void onApplicationEvent(ApplicationEvent applicationEvent) { try { Thread.sleep(3000);//靜靜的沉睡3秒鐘 }catch (Exception e) { e.printStackTrace(); } //轉換事件類型 UserRegisterEvent userRegisterEvent = (UserRegisterEvent) applicationEvent; //獲取注冊用戶對象信息 UserBean user = userRegisterEvent.getUser(); System.out.println("用戶:"+user.getName()+",注冊成功,發(fā)送郵件通知。"); }
下面我們重啟下項目,訪問注冊地址,查看界面反映是否也有延遲。
我們測試發(fā)現(xiàn)訪問界面時反映速度要不之前還要快一些,我們去查看控制臺時,可以看到注冊信息輸出后等待3秒后再才輸出郵件發(fā)送通知,而在這之前界面已經(jīng)做出了反映。
總結注意:如果存在多個監(jiān)聽同一個事件時,并且存在異步與同步同時存在時則不存在執(zhí)行順序。
我們在傳統(tǒng)項目中往往各個業(yè)務邏輯之間耦合性較強,因為我們在service都是直接引用的關聯(lián)service或者jpa來作為協(xié)作處理邏輯,然而這種方式在后期更新、維護性難度都是大大提高了。然而我們采用事件通知、事件監(jiān)聽形式來處理邏輯時耦合性則是可以降到最小。
本章代碼已經(jīng)上傳到碼云:
網(wǎng)頁地址:http://git.oschina.net/jnyqy/lessons
Git地址:https://git.oschina.net/jnyqy/lessons.git
SpringBoot相關系列文章請訪問:目錄:SpringBoot學習目錄
QueryDSL相關系列文章請訪問:QueryDSL通用查詢框架學習目錄
感謝閱讀!
歡迎加入QQ技術交流群,共同進步。
QQ技術交流群
文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/67677.html
摘要:準備好了我們可以開始向中發(fā)布,當發(fā)布后,所有在中的都會收到對應的。將類注入到的中。測試和通過方法將發(fā)布到應用上下文中,同時這個動作會觸發(fā)收到事件。深入剖析發(fā)布與監(jiān)聽的過程在使用方法發(fā)布的時候,最終會調用到中的類的如下的一段代碼。 本篇主要來聊一聊spring中ApplicationListener接口和ApplicationEvent類。 從命名上可以很容易的看出來一個是listene...
摘要:基于版本基于版本。由于中英行文差異,完全的逐字逐句翻譯會很冗余啰嗦。譯者在翻譯中同時參考了谷歌百度有道翻譯的譯文以及編程思想第四版中文版的部分內容對其翻譯死板,生造名詞,語言精煉度差問題進行規(guī)避和改正。 來源:LingCoder/OnJava8 主譯: LingCoder 參譯: LortSir 校對:nickChenyx E-mail: 本書原作者為 [美] Bru...
摘要:第四章安全管理制度發(fā)布第十條安全管理制度必須以正式文件的形式發(fā)布施行。第十一條安全管理制度由信息安全管理小組制訂,信息安全領導小組審批發(fā)布。第二十條安全管理制度的修改與廢止須經(jīng)信息安全領導組織審批確認,信息安全管理部門備案。 字數(shù) 3610閱讀 760評論 0贊 3《xxxx安全管理制度匯編》****制度管理辦法****文...
摘要:寫一個正則表達式來測試變量中是否包含字符串。用函數(shù)給出不使用字符,但和等價的正則表達式。第十四課標志全局匹配標志第二個常用的標志是全局匹配標志,用字母表示。寫出一個正則表達式來檢驗合法性。非捕獲組的主要用途是給一個組賦予量詞。 TRY REGEX 是一個交互式的正則表達式學習項目項目地址:https://github.com/callumacra...在線地址:http://tryre...
摘要:貢獻者飛龍版本最近總是有人問我,把這些資料看完一遍要用多長時間,如果你一本書一本書看的話,的確要用很長時間。為了方便大家,我就把每本書的章節(jié)拆開,再按照知識點合并,手動整理了這個知識樹。 Special Sponsors showImg(https://segmentfault.com/img/remote/1460000018907426?w=1760&h=200); 貢獻者:飛龍版...
閱讀 2086·2021-11-16 11:45
閱讀 582·2021-11-04 16:12
閱讀 1389·2021-10-08 10:22
閱讀 861·2021-09-23 11:52
閱讀 4149·2021-09-22 15:47
閱讀 3525·2021-09-22 15:07
閱讀 497·2021-09-03 10:28
閱讀 1742·2021-09-02 15:21