使用注解定义切面
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()方法可以调用真正的目标方法,从而实现对连接点的完全控制。