實現AOP的切面主要有如下幾個要素:html
@Aspect
註解將一個java類定義爲切面類@Pointcut
定義一個切入點,能夠是一個規則表達式,好比下例中某個package下的全部函數,也能夠是一個註解等。@Before
在切入點開始處切入內容@After
在切入點結尾處切入內容@AfterReturning
在切入點return內容以後切入內容(能夠用來對處理返回值作一些加工處理)@Around
在切入點先後切入內容,並本身控制什麼時候執行切入點自身的內容@AfterThrowing
用來處理當切入內容部分拋出異常以後的處理邏輯
@Aspect @Component public class WebLogAspect { private Logger logger = Logger.getLogger(getClass()); @Pointcut("execution(public * com.didispace.web..*.*(..))") public void webLog(){} @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { // 接收到請求,記錄請求內容 ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); HttpServletRequest request = attributes.getRequest(); // 記錄下請求內容 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 { // 處理完請求,返回內容 logger.info("RESPONSE : " + ret); } }
能夠看上面的例子,經過@Pointcut
定義的切入點爲com.didispace.web
包下的全部函數(對web層全部請求處理作切入點),而後經過@Before
實現,對請求內容的日誌記錄(本文只是說明過程,能夠根據須要調整內容),最後經過@AfterReturning
記錄請求返回的對象。java
經過運行程序並訪問:http://localhost:8080/hello?name=didi
,能夠得到下面的日誌輸出web
2016-05-19 13:42:13,156 INFO WebLogAspect:41 - URL : http://localhost:8080/hello 2016-05-19 13:42:13,156 INFO WebLogAspect:42 - HTTP_METHOD : http://localhost:8080/hello 2016-05-19 13:42:13,157 INFO WebLogAspect:43 - IP : 0:0:0:0:0:0:0:1 2016-05-19 13:42:13,160 INFO WebLogAspect:44 - CLASS_METHOD : com.didispace.web.HelloController.hello 2016-05-19 13:42:13,160 INFO WebLogAspect:45 - ARGS : [didi] 2016-05-19 13:42:13,170 INFO WebLogAspect:52 - RESPONSE:Hello didi
在WebLogAspect切面中,分別經過doBefore和doAfterReturning兩個獨立函數實現了切點頭部和切點返回後執行的內容,若咱們想統計請求的處理時間,就須要在doBefore處記錄時間,並在doAfterReturning處經過當前時間與開始處記錄的時間計算獲得請求處理的消耗時間。函數
那麼咱們是否能夠在WebLogAspect切面中定義一個成員變量來給doBefore和doAfterReturning一塊兒訪問呢?是否會有同步問題呢?優化
的確,直接在這裏定義基本類型會有同步問題,因此咱們能夠引入ThreadLocal對象,像下面這樣進行記錄:spa
@Aspect @Component public class WebLogAspect { private Logger logger = Logger.getLogger(getClass()); ThreadLocal<Long> startTime = new ThreadLocal<>(); @Pointcut("execution(public * com.didispace.web..*.*(..))") public void webLog(){} @Before("webLog()") public void doBefore(JoinPoint joinPoint) throws Throwable { startTime.set(System.currentTimeMillis()); // 省略日誌記錄內容 } @AfterReturning(returning = "ret", pointcut = "webLog()") public void doAfterReturning(Object ret) throws Throwable { // 處理完請求,返回內容 logger.info("RESPONSE : " + ret); logger.info("SPEND TIME : " + (System.currentTimeMillis() - startTime.get())); } }
因爲經過AOP實現,程序獲得了很好的解耦,可是也會帶來一些問題,好比:咱們可能會對Web層作多個切面,校驗用戶,校驗頭信息等等,這個時候常常會碰到切面的處理順序問題。.net
因此,咱們須要定義每一個切面的優先級,咱們須要@Order(i)
註解來標識切面的優先級。i的值越小,優先級越高。假設咱們還有一個切面是CheckNameAspect
用來校驗name必須爲didi,咱們爲其設置@Order(10)
,而上文中WebLogAspect設置爲@Order(5)
,因此WebLogAspect有更高的優先級,這個時候執行順序是這樣的:日誌
在@Before
中優先執行@Order(5)
的內容,再執行@Order(10)
的內容code
@After
和@AfterReturning
中優先執行@Order(10)
的內容,再執行@Order(5)
的內容因此咱們能夠這樣子總結:htm