Spring源碼閱讀目錄
第一部分——IOC篇
第一章 Spring之最熟悉的陌生人——IOC
第二章 Spring之假如讓你來寫IOC容器——加載資源篇
第三章 Spring之假如讓你來寫IOC容器——解析配置文件篇
第四章 Spring之假如讓你來寫IOC容器——XML配置文件篇
第五章 Spring之假如讓你來寫IOC容器——BeanFactory和FactoryBean
第六章 Spring之假如讓你來寫IOC容器——Scope和屬性填充
第七章 Spring之假如讓你來寫IOC容器——屬性填充特別篇:SpEL表達式
第八章 Spring之假如讓你來寫IOC容器——拓展篇
第九章 Spring之源碼閱讀——環境搭建篇
第十章 Spring之源碼閱讀——IOC篇
第二部分——AOP篇
第十一章 Spring之不太熟的熟人——AOP
第十二章 Spring之不得不了解的內容——概念篇
第十三章 Spring之假如讓你來寫AOP——AOP聯盟篇
第十四章 Spring之假如讓你來寫AOP——雛形篇
第十五章 Spring之假如讓你來寫AOP——Joinpoint(連接點)篇
第十六章 Spring之假如讓你來寫AOP——Pointcut(切點)篇
第十七章 Spring之假如讓你來寫AOP——Advice(通知)上篇
第十八章 Spring之假如讓你來寫AOP——Advice(通知)下篇
第十九章 Spring之假如讓你來寫AOP——番外篇:Spring早期設計
第二十章 Spring之假如讓你來寫AOP——Aspect(切面)篇
第二十一章 Spring之假如讓你來寫AOP——Weaver(織入器)篇
第二十二章 Spring之假如讓你來寫AOP——Target Object(目標對象)篇
第二十三章 Spring之假如讓你來寫AOP——融入IOC容器篇
第二十四章 Spring之源碼閱讀——AOP篇
第三部分——事務篇
第二十五章 Spring之曾經的老朋友——事務
第二十六章 Spring之假如讓你來寫事務——初稿篇
第二十七章 Spring之假如讓你來寫事務——鐵三角篇
第二十八章 Spring之假如讓你來寫事務——屬性篇
第二十九章 Spring之假如讓你來寫事務——狀態篇
第三十章 Spring之假如讓你來寫事務——管理篇
第三十一章 Spring之假如讓你來寫事務——融入IOC容器篇
第三十二章 Spring之源碼閱讀——事務篇
第四部分——MVC篇
第三十三章 Spring之夢開始的地方——MVC
第三十四章 Spring之假如讓你來寫MVC——草圖篇
第三十五章 Spring之假如讓你來寫MVC——映射器篇
第三十六章 Spring之假如讓你來寫MVC——攔截器篇
第三十七章 Spring之假如讓你來寫MVC——控制器篇
第三十八章 Spring之假如讓你來寫MVC——適配器篇
第三十九章 Spring之假如讓你來寫MVC——番外篇:類型轉換
第四十章 Spring之假如讓你來寫MVC——ModelAndView篇
第四十一章 Spring之假如讓你來寫MVC——番外篇:數據綁定
第四十二章 Spring之假如讓你來寫MVC——視圖篇
第四十三章 Spring之假如讓你來寫MVC——上傳文件篇
第四十四章 Spring之假如讓你來寫MVC——異常處理器篇
第四十五章 Spring之假如讓你來寫MVC——國際化篇
第四十六章 Spring之假如讓你來寫MVC——主題解析器篇
第四十七章 Spring之假如讓你來寫MVC——閃存管理器篇
第四十八章 Spring之假如讓你來寫MVC——請求映射視圖篇
第四十九章 Spring之假如讓你來寫MVC——番外篇:屬性操作
第五十章 Spring之假如讓你來寫MVC——融入IOC容器篇
第五十一章 Spring之源碼閱讀——MVC篇
文章目錄
- Spring源碼閱讀目錄
- 第一部分——IOC篇
- 第二部分——AOP篇
- 第三部分——事務篇
- 第四部分——MVC篇
- 前言
- 嘗試動手寫IOC容器
- 第三十二版 攔截器
- 攔截器接口
- 改造映射器
- 改造`DispatcherServlet`
- 測試
- 總結
前言
????對于Spring一直都是既熟悉又陌生,說對它熟悉吧,平時用用沒啥問題,但面試的時候被問的一臉懵逼,就很尷尬,都不好意思在簡歷上寫著熟悉Spring了
????所以決定花點時間研究研究Spring的源碼。主要參考的書籍是:《Spring源碼深度解析(第2版)》、《Spring揭秘》、《Spring技術內幕:深入解析Spring架構與設計原理(第2版)》
???? 書接上回,在上篇 第三十五章 Spring之假如讓你來寫MVC——映射器篇 中,A君 已經實現了 映射器 部分的功能了。接下來看看 A君 會有什么騷操作吧
嘗試動手寫IOC容器
????出場人物:A君(苦逼的開發)、老大(項目經理)
????背景:老大 要求 A君在一周內開發個簡單的 IOC容器
????前情提要:A君 已經實現了 映射器 部分的功能了 。。。
第三十二版 攔截器
????今天,剛一上班,A君 就屁顛屁顛的跑到 老大 的辦公室去,炫耀自己的成果
????“嗯。做的不錯。不過還需要加點料?” 老大 看著滿臉興奮的 A君 。悠悠說道
????“加點料,要加什么??” A君 興奮地逐漸消失,一臉懵逼的問到
????“你聽說過 過濾器 嗎?” 老大 微笑著問道
????“聽說過,過濾器 是 Servlet 規范中的一部分,所有 Servlet容器 都必須實現。請求在到達 Servlet 之前,或者響應返回客戶端之前,都會經過 過濾器 進行處理。如果 過濾器 處理不通過,它可以阻止請求繼續往下處理!” A君 回答道
????“不錯,現在要加的料和 過濾器 效果差不多,只是是框架層面的。叫做 攔截器。” 老大 說到
????“為什么有 過濾器 之后還需要 攔截器 呢?” A君 提出疑問
????“問得好!原因其實也很簡單。過濾器 是 Servlet容器 的行為,發生在 Servlet 之前,那么就以為這它無法獲取框架中的內容,無法進行更細致的攔截。” 老大 笑著說道
????“原來如此!” A君 恍然,之前一直存在的疑問,被 老大 三言兩語就解開了
????“去吧!這東西并不難,我希望今天就能看到成果!” 老大 大手一揮,開始下逐客令
攔截器接口
????“OK!” A君 也爽快的回答道,離開辦公室,回到自己的工位上。A君 想都沒想,就開始擼代碼,因為像這種提供拓展的功能,A君 只需要提供接口就行,具體內容由用戶實現即可。A君 新增 HandlerInterceptor
接口,代碼如下:
/*** 攔截器接口*/
public interface HandlerInterceptor {/*** 方法執行前調用** @param request* @param response* @param handler* @return* @throws Exception*/default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {return true;}/*** 方法執行后調用** @param request* @param response* @param handler* @throws Exception*/default void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {}/*** 請求完成時調用,不管成功還是失敗** @param request* @param response* @param handler* @param ex* @throws Exception*/default void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {}
}
好了,接口定義了完成了。不過要如何執行它的實現呢?老大 之前提到和 過濾器 類似,說起 過濾器,A君 其實并不算陌生,之前有折騰過 Tomcat,知道其大致的運行流程。這里又得涉及到一個設計模式——責任鏈。這個模式也好理解,就像 A君 平時想請個假,OA上需要經過層層審批,層層回復一樣:
每一層都得同意,這個假才算請成功,但凡有一個不同意,這個假就算是請失敗了。值得注意的是:請假流程申請的時候是從前往后,而回復的時候卻是從后往前的。責任鏈 與之類似,既然如此,那么 攔截器 也就好辦了:只要把 攔截器 整合成一個鏈表就可以了。A君 添加HandlerExecutionChain
類,代碼如下:
/*** 請求處理鏈*/
@Getter
public class HandlerExecutionChain {/*** 控制器*/private final Object handler;/*** 攔截器集合*/private final List<HandlerInterceptor> interceptorList = new ArrayList<>();private int interceptorIndex = -1;/*** 正向處理,類似與請假申請流程** @param request* @param response* @return* @throws Exception*/boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {for (int i = 0; i < this.interceptorList.size(); i++) {HandlerInterceptor interceptor = this.interceptorList.get(i);if (!interceptor.preHandle(request, response, this.handler)) {//返回false,直接調用完成方法triggerAfterCompletion(request, response, null);return false;}this.interceptorIndex = i;}return true;}/*** 反向處理,類似與請假回復流程** @param request* @param response* @throws Exception*/void applyPostHandle(HttpServletRequest request, HttpServletResponse response)throws Exception {for (int i = this.interceptorList.size() - 1; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptorList.get(i);interceptor.postHandle(request, response, this.handler);}}/*** 反向處理,類似與請假回復流程** @param request* @param response* @throws Exception*/void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Exception ex) {for (int i = this.interceptorIndex; i >= 0; i--) {HandlerInterceptor interceptor = this.interceptorList.get(i);try {interceptor.afterCompletion(request, response, this.handler, ex);} catch (Throwable ex2) {ex2.printStackTrace();}}}//其他方法省略
}
改造映射器
確實如 老大 所說,攔截器 就這么點東西,沒啥難度的。現在還需要改下 映射器 的返回值了,之前是直接返回HandlerMethod
,現在得返回HandlerExecutionChain
了,改動如下:
AbstractHandlerMapping
也做個簡單的改動,需要把配置的 攔截器 添加到HandlerExecutionChain
中,如下:
改造DispatcherServlet
現在基本改造完了,還需要個添加 攔截器 的入口,只需要掃描類是否實現了對應接口就行了。DispatcherServlet
改動如下:
測試
????好嘞,現在一切都準備就緒了。可以開始準備測試了,其他內容還是不需要改動。只需要新增一個 攔截器 即可,A君 新增MyInterceptor
。代碼如下:
public class MyInterceptor implements HandlerInterceptor {// 在請求處理前執行@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("Pre-handle: " + request.getRequestURI());request.setAttribute("message", "Add Interceptor");return true;}// 在請求處理后,視圖渲染前執行@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {System.out.println("Post-handle: " + request.getRequestURI());}// 在請求完全處理完后執行@Overridepublic void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) throws Exception {System.out.println("After completion: " + request.getRequestURI());}
}
添加測試代碼如下:
@Testpublic void v32() throws Throwable {System.out.println("############# 第三十二版: 攔截器篇 #############");Tomcat tomcat = new Tomcat();//設置端口tomcat.setPort(8082);//設置靜態資源路徑String webApp = new File("src/main/resources/v32").getAbsolutePath();tomcat.addWebapp("/test/", webApp);tomcat.start();//掛起tomcat.getServer().await();}
測試結果如下:
前臺成功返回,后臺也成功打印。攔截器 也就這么完成啦。OK!起碼今天可以交差了,看看 老大 明天還有什么想法吧
總結
????正所謂樹欲靜而風不止,欲知后事如何,請看下回分解(?ω?)