1 /** 2 * 使用注解定義切面 3 */ 4 @Aspect 5 public class UserServiceLogger { 6 private static final Logger log = Logger.getLogger(UserServiceLogger.class); 7 8 @Pointcut("execution(* service.UserService.*(..))") 9 public void pointcut() {} 10 11 @Before("pointcut()") 12 public void before(JoinPoint jp) { 13 log.info("調用 " + jp.getTarget() + " 的 " + jp.getSignature().getName() 14 + " 方法。方法入參:" + Arrays.toString(jp.getArgs())); 15 } 16 17 @AfterReturning(pointcut = "pointcut()", returning = "returnValue") 18 public void afterReturning(JoinPoint jp, Object returnValue) { 19 log.info("調用 " + jp.getTarget() + " 的 " + jp.getSignature().getName() 20 + " 方法。方法返回值:" + returnValue); 21 } 22 23 }
@Aspect注解將UserServiceLogger定義為切面,並且使用@Before注解將before()方法定義為前置增強,使用@AfterReturning注解將afterReturning()方法定義為后置增強。為了能夠獲得當前連接點的信息,在增強方法中添加了JoinPoint類型的參數,Spring會自動注入該實例。切入點表達式使用@Pointcut注解來表示,而切入點則需要通過一個普通的方法定義來提供,作為切入點的方法必須返回void類型。
切面定義完后,還需要在Spring配置文件中完成織入工作。
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 8 http://www.springframework.org/schema/context 9 http://www.springframework.org/schema/context/spring-context-3.2.xsd 10 http://www.springframework.org/schema/aop 11 http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"> 12 13 <context:component-scan base-package="service,dao" /> 14 <bean class="aop.UserServiceLogger"></bean> 15 <aop:aspectj-autoproxy /> 16 </beans>
配置文件中受限要導入aop的命名空間。只需在配置文件中添加<aop:aspectj-autoproxy/>元素,就可以啟用對於@AspectJ注解的支持,Spring將自動為匹配的Bean創建代理。
為了注冊定義好的切面,還要在配置文件中聲明UserServiceLogger的一個實例。如果不需要被其他Bean引用,可以不指定id屬性。
1 /** 2 * 通過注解實現異常拋出增強 3 */ 4 @Aspect 5 public class ErrorLogger { 6 private static final Logger log = Logger.getLogger(ErrorLogger.class); 7 8 @AfterThrowing(pointcut = "execution(* service.UserService.*(..))", throwing = "e") 9 public void afterThrowing(JoinPoint jp, RuntimeException e) { 10 log.error(jp.getSignature().getName() + " 方法發生異常:" + e); 11 } 12 13 }
使用@AfterThrowing注解可以定義異常拋出增強。如果需要獲取拋出的異常,可以為增強方法聲明相關類型的參數,並通過@AfterThrowing注解的throwing屬性指定該參數名稱,Spring會為其注入從目標方法拋出的異常實例。
1 /** 2 * 通過注解實現最終增強 3 */ 4 @Aspect 5 public class AfterLogger { 6 private static final Logger log = Logger.getLogger(AfterLogger.class); 7 8 @After("execution(* service.UserService.*(..))") 9 public void afterLogger(JoinPoint jp) { 10 log.info(jp.getSignature().getName() + " 方法結束執行。"); 11 } 12 13 }
@After注解可以定義最終增強。
1 /** 2 * 通過注解實現環繞增強 3 */ 4 @Aspect 5 public class AroundLogger { 6 private static final Logger log = Logger.getLogger(AroundLogger.class); 7 8 @Around("execution(* service.UserService.*(..))") 9 public Object aroundLogger(ProceedingJoinPoint jp) throws Throwable { 10 log.info("調用 " + jp.getTarget() + " 的 " + jp.getSignature().getName() 11 + " 方法。方法入參:" + Arrays.toString(jp.getArgs())); 12 try { 13 Object result = jp.proceed(); 14 log.info("調用 " + jp.getTarget() + " 的 " 15 + jp.getSignature().getName() + " 方法。方法返回值:" + result); 16 return result; 17 } catch (Throwable e) { 18 log.error(jp.getSignature().getName() + " 方法發生異常:" + e); 19 throw e; 20 } finally { 21 log.info(jp.getSignature().getName() + " 方法結束執行。"); 22 } 23 } 24 25 }
@Around注解可以定義環繞增強。通過為增強方法聲明ProceedingJoinPoint類型的參數,可以獲得連接點信息。通過它的proceed()方法可以調用真正的目標方法,從而實現對連接點的完全控制。