【JavaEE】AOP(2)
【JavaEE】AOP(2)
在前面的Spring AOP的學習之中,Spring AOP去實現(xiàn)AOP,雖然比較靈活,可以實現(xiàn)很多想法,但是也有一些現(xiàn)實的問題:
-
沒辦法獲取到HttpRequest,一些功能難以實現(xiàn)
- 進而無法獲取HttpSession對象,這樣登錄校驗功能就無法實現(xiàn)
- 我們要對?部分方法進行攔截,而另?部分方法不攔截,如注冊方法和登錄方法是不攔截的,這樣的話排除方法的規(guī)則很難定義,甚至沒辦法定義
我們手動去實現(xiàn),太難了!
所以本文講解的重點就是,Spring全家桶實現(xiàn)AOP的幾種固定的機制,雖然較不靈活,但是實現(xiàn)的功能有針對性,且代碼簡單!
- 只要我們按照框架的要求去寫,就可以實現(xiàn)對應的功能
- 這些功能都是很常見、很必要的功能!
- 所以不要糾結(jié)于這個框架不夠全面,不能實現(xiàn)一些極端特殊的要求,因為它只是 把一切常見的功能,打包成現(xiàn)成的模塊 ,方便開發(fā);如果有其他特殊要求,用Spring AOP等等偏底層地去實現(xiàn)就好了!
- 框架就是這樣的,方便一些常見開發(fā)的操作,我們不需要過多了解底層實現(xiàn),“坐享其成”即可??
- Spring全家桶實現(xiàn)AOP更偏向于具體的統(tǒng)一處理功能,而Spring AOP實現(xiàn)AOP更偏底層
網(wǎng)路資料:
Spring AOP是Spring框架中的一個模塊,用于實現(xiàn)面向切面編程。它通過動態(tài)代理或者字節(jié)碼生成的方式,在運行時將橫切邏輯織入到目標對象的方法中,從而在目標對象執(zhí)行前、執(zhí)行后或者執(zhí)行異常時執(zhí)行額外的操作。Spring AOP主要基于代理模式來實現(xiàn)。
Spring Boot框架是基于Spring框架的擴展,用于簡化和加速Spring應用程序的開發(fā)。
Spring Boot并沒有重新實現(xiàn)AOP概念,而是直接繼承了Spring框架中的AOP模塊,因此在Spring Boot中,使用AOP的方式和Spring框架中的方式是一樣的。Spring Boot提供了更方便的自動配置和默認約定,使得使用AOP更加簡單。
因此,Spring AOP和Spring Boot框架實現(xiàn)的AOP的本質(zhì)是相同的,都是通過代理方式實現(xiàn)的面向切面編程。區(qū)別主要在于Spring全家桶提供了[更簡化的配置]和[默認約定],使得使用AOP更加方便。
- 因為代碼的運行就是通過 Spring Boot框架來運行的,以此來控制一些邏輯也很合理!
本文講解的幾個統(tǒng)一處理:
-
統(tǒng)一登錄校驗處理
- Spring 攔截器
- 統(tǒng)一異常處理
- 統(tǒng)一數(shù)據(jù)格式返回處理
沒學到不代表不重要,需要用到什么統(tǒng)一處理的功能,再專門去學!
1. 統(tǒng)一登錄校驗處理
框架的學習說到底就是學習一個約定,按照約定才能實現(xiàn)方便功能~
Spring全家桶提供了具體的現(xiàn)成攔截器:HandlerInterceptor ,應用攔截器分為兩步:
-
創(chuàng)建自定義攔截器,實現(xiàn) HandlerInterceptor接口,重寫 preHandle方法
-
preHandle()
=> 目標方法執(zhí)行之前要執(zhí)行的預處理方法 -
其返回值為true/false
-
true=>目標方法可執(zhí)行,放行
-
false=>目標方法不可執(zhí)行,攔截
//偽代碼 HandlerInterceptor o = new LoginInterceptor(); if(!o.preHandle(...)) { return; } targetMethod();
-
-
-
重寫 WebMvcConfigurer 的
addInterceptors(InterceptorRegistry registry)
方法-
添加攔截器到registry中(登記)
- 可以添加多個攔截器,按順序進行攔截(如果邏輯沖突,按照順序,以第一個結(jié)果為準)
- 添加攔截的路由/排除攔截的路由
-
添加攔截器到registry中(登記)
1.1 自定義攔截器
Ctrl O重寫方法:
默認返回true,放行
Java對于一些常量通常會放在一個類里面,以訪問的形式去獲取常量,而不是每次都手敲:
1.2 將自定義攔截器加入到系統(tǒng)配置
Ctrl O重寫方法:
- 加Configuration!否則項目啟動不會加載這個類,自然不會進行系統(tǒng)配置
- 實現(xiàn)MebMvcConfigurer,自然就是跟Spring Mvc(網(wǎng)絡相關(guān))有關(guān)的配置
- 不僅僅只有配置文件才能配置,配置類也可以哦!
攔截器對象加入到Spring容器里
一般寫成這樣:
PathPatterns,路徑的樣式
其實直接new一個咱們的攔截器對象甚至匿名內(nèi)部類重寫那個方法也行,因為這個參數(shù)只是定義預處理方法
這個類只是游離在外的普通類,一個攔截器的定義罷了~
為什么可以怎么寫:
- 其實就是因為每個加或者減后返回的都是同一個對象~
1.3 測試
為了突出結(jié)果,做出以下改動:
- 攔截成功跳轉(zhuǎn)百度:
- 提示執(zhí)行了攔截器
- 排除路由/test/say_hello
效果:
1.4 對于靜態(tài)資源的處理
比如圖片/html/js/css等資源,如果是服務器的,都是向服務器請求來的
-
html等等很常規(guī)無非就是:
.excludePathPatterns("/**/*.html")
-
但是圖片不一樣,圖片的格式很多很多,我們沒辦法一一列舉,而我們可以這么做:
- 將圖片保存到一個目錄image里,然后排除整個image目錄
.excludePathPatterns("/image/**")
1.5 小練習:統(tǒng)一登錄攔截處理
將我們之前做的博客前端資源拷貝到static里:
后端基本上代碼不變,主要是對靜態(tài)資源和一些路由的攔截排除:
有可能有緩存的問題,剛才的資源沒有出現(xiàn)在target中,所以要刪掉重新啟動
效果:
重定向只是 對應的路徑 相當于重定向后的路徑,所以如果只是請求資源或者請求響應數(shù)據(jù),不是訪問 對應的路徑 的網(wǎng)頁,是不會觸發(fā)跳轉(zhuǎn)的~
- 只會表現(xiàn)為訪問不到資源/響應狀態(tài)碼為302
例如:
- 背景消失~
補充,靜態(tài)資源以static為起始,接受多級目錄訪問:
注意刪除target重新啟動!
1.6 攔截器原理
1.6.1 執(zhí)行流程
之前:
現(xiàn)在:
- 就在請求的傳遞路上"截胡"
1.6.2 源碼分析
所有的Controller執(zhí)行都會通過一個調(diào)度器DispatcherServlet來實現(xiàn),可以通過控制臺的日志觀察到:(前提是攔截器工作了?。?/p>
雙擊shift查找DispatcherServlet:
- 這個就是我們重寫的方法!
- 循環(huán)攔截器集合(多個攔截器的情況)
true就可以繼續(xù)進行后面的程序(我們的目標方法就在后面)
false就直接return(響應通過輸出型參數(shù)response返回)
- 客戶端發(fā)送請求后,在Spring MVC框架中會經(jīng)過一系列的組件處理,其中就有可能被攔截器 “截胡”
1.7 擴展:統(tǒng)一訪問前綴添加
所有請求地址添加api前綴:
@Configuration//不是Configurable!
public class AppConfig implements WebMvcConfigurer {
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
configurer.addPathPrefix("api", c -> true);
}
}
- api,原有路由多一級
- 但是不代表默認不被攔截!
- 還是要去排除/api/…
- 還會導致原有路由不能用
- c -> true,是一個表達式(不需要了解,感興趣可以了解),表示啟動前綴,false則無效果
2. 統(tǒng)一異常處理
2.1 為什么需要異常處理
如果后端報異常:
(監(jiān)視器已關(guān)掉,此處重點觀察異常)
信息太過雜,我們需要總結(jié)要點,但是不能邊開發(fā)邊寫這些異常處理操作,而且?guī)讉€開發(fā)者寫的可能都不一樣,后期也不方便維護,所以要統(tǒng)一處理~
所以我們希望異常返回的時候應該有明確統(tǒng)一的信息!
網(wǎng)路資料(了解):
錯誤處理的一致性:通過統(tǒng)一處理異常,可以確保應用程序在遇到異常時采取一致的處理方式。這樣可以提供更好的用戶體驗,避免不一致的錯誤展示或處理方式。
代碼的可維護性:通過將異常處理邏輯集中到一個地方,可以提高代碼的可維護性。如果每個控制器方法都有自己的異常處理邏輯,會導致代碼重復和冗余,難以維護和更新。
封裝敏感信息:統(tǒng)一處理異??梢詭椭覀兎庋b敏感信息,防止泄露給客戶端。在異常處理過程中,我們可以選擇性地披露或隱藏敏感信息,并返回友好的錯誤消息給客戶端。
日志記錄和監(jiān)控:通過統(tǒng)一處理異常,我們可以方便地進行日志記錄和監(jiān)控。在異常處理過程中,我們可以記錄異常的詳細信息,包括異常類型、堆棧跟蹤等,以便后續(xù)的故障排查和系統(tǒng)監(jiān)控。
異常轉(zhuǎn)換和包裝:有時候,我們可能需要將底層的異常轉(zhuǎn)換為更高級別的異常,或者將多個異常包裝為一個更通用的異常。通過統(tǒng)一處理異常,可以方便地進行這些異常轉(zhuǎn)換和包裝操作,以便更好地傳遞和處理異常。
錯誤頁面的統(tǒng)一管理:通過統(tǒng)一處理異常,可以將錯誤頁面的管理集中到一個地方。這樣可以更方便地自定義錯誤頁面,并確保所有的異常都有對應的錯誤展示界面。
總之,通過統(tǒng)一處理異常,我們可以提高代碼的可維護性和可讀性,封裝敏感信息,方便日志記錄和監(jiān)控,進行異常轉(zhuǎn)換和包裝,以及統(tǒng)一管理錯誤頁面。這些都有助于提升應用程序的質(zhì)量和用戶體驗。
大概了解一下就行了,實際項目遇到去使用體驗會更好!
2.2 應用異常處理器
我們?nèi)匀挥鞋F(xiàn)成的異常處理器可以用,如何應用:
-
@ControllerAdvice
- 加在類上,代表其為控制器通知類此類會“監(jiān)聽”后端環(huán)境,隨著項目啟動的加載,如果拋出異常會被此類感知到
-
@ExceptionHandler(Exception.class)
- 顧名思義,這就是異常處理器,加在方法上,其返回值就是格式化后的數(shù)據(jù),注解的括號內(nèi)應該為對應異常的類對象,并且這個方法可以有一個參數(shù)e(名字自?。?,e會被注入剛才的異常對象
- 會在獲取到異常后調(diào)用這個方法,(AOP advice的位置)
補充:
返回的格式以json為最佳,所以返回的方式有兩種:
- 自定義對象/自定義對象集合
- Map對象,自定義映射關(guān)系
- 方便,以這個為例
返回值的類型可以是Object,包括其泛型或者其屬性,系統(tǒng)會自動去獲取每一個都是誰向上轉(zhuǎn)型而來的
- 無論什么異常都會被這個異常處理器捕捉到,因為Exception是所有異常的父類,而參數(shù)e則是被向上轉(zhuǎn)型來的,所以其getMessage方法被重寫了,仍然可以正常使用
- 隨便寫的,只是示例!
但是對于潛在的個別異常,如NullPointerException,應該單獨處理,并且被子類捕獲就不會被父類捕獲了~
- 因為異常太多了,所以還是需要Exception這個"老父親"來兜底
效果:
其他異常效果:
3. 統(tǒng)一數(shù)據(jù)格式返回處理
3.1 為什么要統(tǒng)一數(shù)據(jù)格式
其實每個公司都會對字段名嚴格必須符合規(guī)范,但是返回的格式則千奇百怪
統(tǒng)一數(shù)據(jù)格式的優(yōu)點很多,比如以下幾個:
- 方便前后端程序員對接數(shù)據(jù)
- 降低前后端溝通成本,所有接口都按一個格式返回
- 有利于項目統(tǒng)一數(shù)據(jù)的維護和修改
- 有利于后端技術(shù)部門的統(tǒng)一規(guī)范,不會出現(xiàn)稀奇古怪的返回內(nèi)容
3.2 數(shù)據(jù)格式統(tǒng)一方法
一樣可以按照剛才的兩種方式:
- 自定義對象
- 本次演示這種,因為有個小坑
- Map
現(xiàn)在我們分為兩種情況:
- 成功是一個方法
- 失敗是一個方法
3.3 應用數(shù)據(jù)格式返回統(tǒng)一處理器
我們?nèi)匀挥鞋F(xiàn)成的數(shù)據(jù)格式返回統(tǒng)一處理器可以用,如何應用:
-
@ControllerAdvice
- 加在類上,代表其為控制器通知類此類會“監(jiān)聽”后端環(huán)境,隨著項目啟動的加載,如果有返回值就會被捕獲到!
-
實現(xiàn)接口ResponseBodyAdvice
-
重寫supports方法,表示我們是否要重寫beforeBodyWrite方法
- 通過這個方法以及參數(shù)我們可以動態(tài)的選擇性的對一些控制器和方法不重寫/重寫
- return true代表可以重寫,反之不能重寫
-
重寫beforeBodyWrite方法
- 顧名思義就是“篡改”返回值,這個方法會在方法返回之前調(diào)用,替原方法返回值,(AOP advice的位置)
- 默認返回像加了@ResponseBody
-
重寫supports方法,表示我們是否要重寫beforeBodyWrite方法
測試:
- 返回值類型已經(jīng)不重要了,因為都會被返回值統(tǒng)一處理掉
- 當然寫原來的類型會更好,因為這樣有利于代碼的正確性,并且此接口可能被處理器排除了~
效果:
3.4 String類型無法處理的問題
- 因為異常處理,把異常處理的信息再次看成返回值了,實際上返回值處理器運行了兩次??
因為框架在對返回數(shù)據(jù)格式處理的時候分為兩類:
- body是非String類型向上轉(zhuǎn)型而來
- 跟原來一樣,返回值Result類型可以直接返回,框架自動轉(zhuǎn)換為json字符串
-
body是String類型向上轉(zhuǎn)型而來
- 也跟原來一樣,應該返回String類型并且視作html代碼,Result類型的返回值嘗試轉(zhuǎn)換為String類型,但是Result類型無法直接轉(zhuǎn)換為String類型!
- 因為框架【并沒有】將它看成非String對象返回,然后按非String對象的方式轉(zhuǎn)換為json字符串!
-
然后把Result對象看成String對象返回,顯然直接報異常!
- 證據(jù)就是,Result對象是完好的,只要調(diào)用toString方法返回,不會報異常,但是直接返回Result會報異常。說明返回值必須是字符串,并且最終會被渲染
所以我們應該對String類型單獨處理:
效果:
在瀏覽器中,這段字符串被認為是html:
可見剛才的結(jié)論~因為響應中Content-Type并非application/json
- 因此前端收到這個響應,并不會直接將body視作json對象,而是字符串(html代碼),所以我們要用jquery的現(xiàn)成方法轉(zhuǎn)化為json對象才行
3.5 @ControllerAdvice 源碼分析
跟其他類注解一樣派生于@Component,而所有的組件都會調(diào)用InitializingBean接口:
Spring MVC中的RequestMappingHandlerAdapter,實現(xiàn)了它:
實現(xiàn)了afterPropertiesSet方法:
異常處理器和返回值處理器的核心前提就是有這個注解,原因歸功于它的監(jiān)聽機制
- 我們發(fā)現(xiàn),這些ControllerAdvice注解類Bean對象特殊地被查找和存儲在容器之中,它們比較“忙”,當發(fā)生某個事件的時候,調(diào)用對應的Advice方法,比如統(tǒng)一異常處理,統(tǒng)一返回值格式處理…
文章到此結(jié)束!謝謝觀看
可以叫我 小馬,我可能寫的不好或者有錯誤,但是一起加油鴨??!本文重點講基本使用方法,具體功能實現(xiàn)要結(jié)合實際應變!文章來源:http://www.zghlxwxcb.cn/news/detail-678614.html
- 例如攔截后咋樣,異常處理格式咋樣,統(tǒng)一數(shù)據(jù)格式返回咋樣…都不是固定的,是根據(jù)實際情況的!
代碼:spring_boot_aop_demo/src/main/java/com/example/demo · 游離態(tài)/馬拉圈2023年8月 - 碼云 - 開源中國 (gitee.com)文章來源地址http://www.zghlxwxcb.cn/news/detail-678614.html
到了這里,關(guān)于【JavaEE】Spring全家桶實現(xiàn)AOP-統(tǒng)一處理的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!