前言
把文章在簡書同步了之后强饮,有幾篇文章被收入專題邮丰,還有一篇文章被收入首頁铭乾,盡管不是什么了不起的事妈经,但是這樣也讓自己挺開心的吹泡,慕課網(wǎng)約我錄了一次視頻課程经瓷,沒有通過揭朝,讓修改下錄第二次色冀,想想還是算了锋恬,目前的自己水平還是有限彤悔,另一方面時間也比較有限晕窑,在博客這方面能夠持續(xù)下去就已經(jīng)是件很難的事情,其他的以后再說看緣分吧敞斋。接下來繼續(xù)技術(shù)的分析渺尘。
前兩篇主要記錄了AOP所用的核心設(shè)計模式-代理模式,包含動態(tài)代理和靜態(tài)代理盔沫,以及JDK和CGLIB的兩種實現(xiàn)方式医咨,接下來開始重點分析Spring-AOP源碼相關(guān)操作,當(dāng)然開始也是從概念理解架诞。這個不太好明白拟淮,但是第一遍閱讀也主要是為了對于框架有個大局觀,不需要過度的拘泥于細(xì)節(jié)谴忧。AOP這段計劃在9月中之前結(jié)束很泊,而下半月主要是SpringMVC相關(guān)的源碼研究,可能也會結(jié)合一點事務(wù)和mybatis源碼的研究沾谓。
參考了很多程序員DD的東西
Spring-Aop相關(guān)概念如下:
我把這個圖片放在網(wǎng)絡(luò)上了委造,歡迎大家前往下載,建議大家先從地址一下載均驶,地址二要耗流量
下載地址
備用地址
相關(guān)的概念描述在腦圖中有詳細(xì)解釋昏兆,當(dāng)然方便理解,這里我也會記錄下來。
- Target(目標(biāo)對象):需要被代理增強的對象
- Proxy(代理對象):目標(biāo)對象被AOP 織入 增強/通知后,產(chǎn)生的對象.
- Joinpoint(連接點):指那些被攔截到的點.在Spring中,這些點指方法(因為Spring只支持方法類型的連接點).
- Pointcut(切入點):指需要(配置)被增強的Joinpoint.
- Advice(通知/增強):指攔截到Joinpoint后要做的操作.通知分為前置通知/后置通知/異常通知/最終通知/環(huán)繞通知等.
- Aspect(切面):切入點和通知的結(jié)合。
- Weaving(織入):指把增強/通知應(yīng)用到目標(biāo)對象來創(chuàng)建代理對象的過程(Spring采用動態(tài)代理織入,AspectJ采用編譯期織入和類裝載期織入).
- Introduction(引入增強):一種特殊通知,在不修改類代碼的前提下,可以在運行期為類動態(tài)地添加一些Method/Field(不常用).
項目部署
我們先在springboot中將aop整合進(jìn)來
在pom.xml引入pom依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
starter中默認(rèn)添加了@EnableAspectJAutoProxy
設(shè)計一個簡單的controller入門
@RestController
public class HomeController {
private Logger logger = Logger.getLogger(getClass());
@GetMapping("/index")
public String index(@RequestParam String name){
logger.info("-----------{name}:{}"+name);
return "【welcome to aop】:" +name;
}
}
實現(xiàn)Web層的日志切面
- 使用@Aspect注解將一個java類定義為切面類
- 使用@Pointcut定義一個切入點碘举,可以是一個規(guī)則表達(dá)式蝙场,比如下例中某個package下的所有函數(shù)台诗,也可以是一個注解等粱快。根據(jù)需要在切入點不同位置的切入內(nèi)容
- 使用@Before在切入點開始處切入內(nèi)容
- 使用@After在切入點結(jié)尾處切入內(nèi)容
- 使用@AfterReturning在切入點return內(nèi)容之后切入內(nèi)容(可以用來對處理返回值做一些加工處理)
- 使用@Around在切入點前后切入內(nèi)容慷蠕,并自己控制何時執(zhí)行切入點自身的內(nèi)容
- 使用@AfterThrowing用來處理當(dāng)切入內(nèi)容部分拋出異常之后的處理邏輯
代碼如下:
@Aspect
@Component
public class WebLogAspect {
private Logger logger = Logger.getLogger(getClass());
@Pointcut("execution(public * com.sunliangliang.springsource.controller..*(..))")
public void webLog(){}
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 接收到請求剑辫,記錄請求內(nèi)容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 記錄下請求內(nèi)容
logger.info("URL : " + request.getRequestURL().toString());
logger.info("HTTP_METHOD : " + request.getMethod());
logger.info("IP : " + request.getRemoteAddr());
logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
}
@AfterReturning(returning = "ret", pointcut = "webLog()")
public void doAfterReturning(Object ret) throws Throwable {
// 處理完請求编整,返回內(nèi)容
logger.info("RESPONSE : " + ret);
}
}
- 通過@Pointcut定義切入點為
com.sunliangliang.springsource.controller.
包下面的所有函數(shù)汞斧,然后通過@Before
實現(xiàn),對請求內(nèi)容的日志記錄(本文只是說明過程剑勾,可以根據(jù)需要調(diào)整內(nèi)容)谣拣,最后通過@AfterReturning
記錄請求返回的對象贵涵。
運行程序
輸出如下日志:
2017-09-01 18:20:34.953 INFO 14696 --- [nio-8888-exec-3] c.s.springsource.aop.WebLogAspect : URL : http://localhost:8888/index
2017-09-01 18:20:34.953 INFO 14696 --- [nio-8888-exec-3] c.s.springsource.aop.WebLogAspect : HTTP_METHOD : GET
2017-09-01 18:20:34.953 INFO 14696 --- [nio-8888-exec-3] c.s.springsource.aop.WebLogAspect : IP : 0:0:0:0:0:0:0:1
2017-09-01 18:20:34.953 INFO 14696 --- [nio-8888-exec-3] c.s.springsource.aop.WebLogAspect : CLASS_METHOD : com.sunliangliang.springsource.controller.HomeController.index
2017-09-01 18:20:34.953 INFO 14696 --- [nio-8888-exec-3] c.s.springsource.aop.WebLogAspect : ARGS : [liangliang]
2017-09-01 18:20:34.953 INFO 14696 --- [nio-8888-exec-3] c.s.s.controller.HomeController : -----------{name}:{}liangliang
2017-09-01 18:20:34.953 INFO 14696 --- [nio-8888-exec-3] c.s.springsource.aop.WebLogAspect : RESPONSE : 【welcome to aop】:liangliang
優(yōu)化
由于通過AOP實現(xiàn)片林,程序得到了很好的解耦,但是也會帶來一些問題,比如:我們可能會對Web層做多個切面阻塑,校驗用戶迈窟,校驗頭信息等等,這個時候經(jīng)常會碰到切面的處理順序問題凳寺。
所以要定義每個切面的優(yōu)先級,我們需要@Order(i)注解來標(biāo)識切面的優(yōu)先級。i值越小杆融,優(yōu)先級越高池摧。假設(shè)我們還有一個切面是CheckNameAspect用來校驗name必須為didi,我們?yōu)槠湓O(shè)置@Order(10),而上文中WebLogAspect設(shè)置為@Order(5)浙踢,所以WebLogAspect有更高的優(yōu)先級思瘟,這個時候執(zhí)行順序是這樣的:
在@Before中優(yōu)先執(zhí)行@Order(5)的內(nèi)容光绕,再執(zhí)行@Order(10)的內(nèi)容
在@After和@AfterReturning中優(yōu)先執(zhí)行@Order(10)的內(nèi)容菇晃,再執(zhí)行@Order(5)的內(nèi)容
所以我們可以這樣子總結(jié):在切入點前的操作甲捏,按order的值由小到大執(zhí)行
在切入點后的操作,按order的值由大到小執(zhí)行
代碼完整示例