AspectJ是一個面向切面的框架,它擴展了Java語言、定義了AOP語法,可以在編譯期提供代碼的織入,它提供了一個專門的編譯期用來生成遵照字節編碼規範的Class文件。java
@Aspect是AspectJ 5新增的功能,使用JDK 5.0註解技術和正規的AspectJ切點表達式語言描述切面。所以在使用@Aspect以前,須要保證所使用的JDK是5.0或更高版本,不然將沒法使用註解技術。spring
Spring經過集成AspectJ實現了以註解的方式定義切面,大大減輕了配置文件的工做量。此外,由於Java的反射機制沒法獲取方法參數名,Spring還須要利用輕量級的字節碼處理框架asm(已集成在Spring Core模塊中)處理@AspectJ中所描述的方法參數名。框架
package edu.cn.service; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; @Aspect public class UserServiceLogger { private static final Logger log = LoggerFactory.getLogger(UserServiceLogger.class); @Before("execution(* edu.cn.service.UserService.*(..))") public void before(JoinPoint jp){ log.info("調用" + jp.getTarget() + "的" + jp.getSignature().getName() + "方法。方法入參:" + Arrays.toString(jp.getArgs())); } @AfterReturning(pointcut = "execution(* edu.cn.service.UserService.*(..))", returning = "returnValue") public void afterReturning(JoinPoint jp, Object returnValue){ log.info("調用" + jp.getTarget() + "的" + jp.getSignature().getName() + "方法。方法返回值:" + returnValue); } }
@Aspect等註解在aspectjweaver依賴下。在上述代碼中,使用@Aspect註解將UserServiceLogger定義爲切面,而且使用@Before註解將before()方法定義爲前置加強,使用@AfterReturning方法將afterReturning()方法定於爲後置加強。編碼
爲了可以得到當前鏈接點的信息,在加強方法中添加了JoinPoint類型的參數,Spring會自動注入該實例。spa
對於後置加強,還能夠定義一個參數用於接受目標方法的返回值。須要注意的是,必須在@AfterReturning註解中經過returning屬性指定該參數的名稱,Spring會將目標方法的返回值賦值給指定名稱的參數。代理
package edu.cn.service; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; @Aspect public class UserServiceLogger { private static final Logger log = LoggerFactory.getLogger(UserServiceLogger.class); @Pointcut("execution(* edu.cn.service.UserService.*(..))") public void pointcut(){} @Before("pointcut()") public void before(JoinPoint jp){ log.info("調用" + jp.getTarget() + "的" + jp.getSignature().getName() + "方法。方法入參:" + Arrays.toString(jp.getArgs())); } @AfterReturning(pointcut = "pointcut()", returning = "returnValue") public void afterReturning(JoinPoint jp, Object returnValue){ log.info("調用" + jp.getTarget() + "的" + jp.getSignature().getName() + "方法。方法返回值:" + returnValue); } }
切入點表達式能夠使用@Pointcut註解來表示,而切入點簽名則須要經過一個普通的方法定義來提供,如上述代碼中的pointcut()方法,做爲切入點簽名的方法必須返回void類型。定義好切入點後,就能夠使用「pointcut()」簽名進行引用日誌
只需在配置文件中添加<aop:aspectj-autoproxy/>元素,就能夠啓用對於@AspectJ註解的支持,Spring將自動爲匹配的Bean建立代理code
爲了註冊定義好的切面,還需在Spring配置文件中聲明UserServiceLogger的一個實例。若是不須要被其餘Bean引用,能夠不指定id屬性component
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="service,dao"/> <bean class="edu.cn.service.UserServiceLogger"></bean> <aop:aspectj-autoproxy/> </beans>
使用@AfterThrowing註解能夠定義異常拋出加強。若是須要獲取拋出的異常,能夠爲加強方法聲明相關類型的參數,並經過@AfterThrowing註解的throwing屬性指定該參數名稱,Spring會爲其注入從目標方法拋出的異常實例,以下xml
package edu.cn.service; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Aspect public class ErrorLogger { private static final Logger log = LoggerFactory.getLogger(ErrorLogger.class); @AfterThrowing(pointcut = "execution(* edu.cn.service.UserService.*(..))", throwing = "e") public void afterThrowing(JoinPoint jp, RuntimeException e){ log.error(jp.getSignature().getName() + "方法方法異常:" + e); } }
使用@After註解能夠定義最終加強
package edu.cn.service; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Aspect public class AfterLogger { private static final Logger log = LoggerFactory.getLogger(AfterLogger.class); @After("execution(* edu.cn.service.UserService.*(..))") public void afterLogger(JoinPoint jp){ log.info(jp.getSignature().getName() + "方法結束執行。"); } }
使用@Around註解能夠定義環繞加強。經過爲加強方法聲明ProceedingJoinPoint類型的參數,能夠得到鏈接點信息。經過它的proceed()方法能夠調用真正的目標方法,從而實現對鏈接點的徹底控制。
package edu.cn.service; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.util.Arrays; @Aspect public class AroundLogger { private static final Logger log = LoggerFactory.getLogger(AroundLogger.class); @Around("execution(* edu.cn.service.UserService.*(..))") public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable { log.info("調用" + jp.getTarget() + "的" + jp.getSignature().getName() + "方法。方法入參:" + Arrays.toString(jp.getArgs())); try { Object result = jp.proceed(); log.info("調用" + jp.getTarget() + "的" + jp.getSignature().getName() + "方法。方法返回值:" + result); } catch (Throwable e) { log.error(jp.getSignature().getName() + "方法發生異常:" + e); throw e; }finally { log.info(jp.getSignature().getName() + "方法結束執行。"); } } }