017-Spring Boot AOP
一、概述
面向切面编程,将业务代码与处理琐碎相关度少的代码隔离开。以便达到重用,解耦。
用途:日志记录、权限处理、性能统计、监控、事务处理、异常处理等
通知类型有:前置通知、后置最终通知、后置返回通知、后置异常通知、环绕通知,
二、使用
1》增加POM依赖,默认已经启用aop,不需要增加注解等
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
2》创建一个Aspect切面类,并指定切点::
封装横切关注点(日志,监控等),需要配置通知(前置通知,后置通知)和切入点(那些包哪些类的哪些方法等)
@Aspect @Component public class LogAspect {
// 配置了com.lhx.spring.springboot.dao包及其子包下的所有类的所有方法 @Before("execution (* com.lhx.spring.springboot.dao..*.*(..))") public void executeService() { System.out.println("method executeService done"); } }
2.1》前置通知 Before
/** * 前置通知,方法调用前被调用 * @param joinPoint */ @Before("executeService()") public void doBeforeAdvice(JoinPoint joinPoint){ System.out.println("我是前置通知!!!"); //获取目标方法的参数信息 Object[] obj = joinPoint.getArgs(); //AOP代理类的信息 joinPoint.getThis(); //代理的目标对象 joinPoint.getTarget(); //用的最多 通知的签名 Signature signature = joinPoint.getSignature(); //代理的是哪一个方法 System.out.println(signature.getName()); //AOP代理类的名字 System.out.println(signature.getDeclaringTypeName()); //AOP代理类的类(class)信息 signature.getDeclaringType(); //获取RequestAttributes RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); //从获取RequestAttributes中获取HttpServletRequest的信息 HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST); //如果要获取Session信息的话,可以这样写: //HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION); Enumeration<String> enumeration = request.getParameterNames(); Map<String,String> parameterMap = Maps.newHashMap(); while (enumeration.hasMoreElements()){ String parameter = enumeration.nextElement(); parameterMap.put(parameter,request.getParameter(parameter)); } String str = JSON.toJSONString(parameterMap); if(obj.length > 0) { System.out.println("请求的参数信息为:"+str); } }
注意:这里用到了JoinPoint和RequestContextHolder。通过JoinPoint可以获得通知的签名信息,如目标方法名、目标方法参数信息等。通过RequestContextHolder来获取请求信息,Session信息。
2.2》后置返回通知
/** * 后置返回通知 * 这里需要注意的是: * 如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息 * 如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数 * returning 限定了只有目标方法返回值与通知方法相应参数类型时才能执行后置返回通知,否则不执行,对于returning对应的通知方法参数为Object类型将匹配任何目标返回值 * @param joinPoint * @param keys */ @AfterReturning(value = "execution(* com.lhx.spring.springboot.dao..*.*(..))",returning = "keys") public void doAfterReturningAdvice1(JoinPoint joinPoint,Object keys){ System.out.println("第一个后置返回通知的返回值:"+keys); } @AfterReturning(value = "execution(* com.lhx.spring.springboot.dao..*.*(..))",returning = "keys",argNames = "keys") public void doAfterReturningAdvice2(String keys){ System.out.println("第二个后置返回通知的返回值:"+keys); }
调用方法
public String testAfterReturning(String key){ return "key=: "+key; } public Integer testAfterReturning01(Integer key){ return key; }
当入参key=testsss时
第一个后置返回通知的返回值:key=: testsss
第二个后置返回通知的返回值:key=: testsss
当入参key=55553时
第一个后置返回通知的返回值:55553
2.3》后置异常通知
/** * 后置异常通知 * 定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法; * throwing 限定了只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行, * 对于throwing对应的通知方法参数为Throwable类型将匹配任何异常。 * @param joinPoint * @param exception */ @AfterThrowing(value = "executeService()",throwing = "exception") public void doAfterThrowingAdvice(JoinPoint joinPoint,Throwable exception){ //目标方法名: System.out.println(joinPoint.getSignature().getName()); if(exception instanceof NullPointerException){ System.out.println("发生了空指针异常!!!!!"); } }
2.4》后置最终通知
/** * 后置最终通知(目标方法只要执行完了就会执行后置通知方法) * @param joinPoint */ @After("executeService()") public void doAfterAdvice(JoinPoint joinPoint){ System.out.println("后置通知执行了!!!!"); }
2.5》环绕通知
/** * 环绕通知: * 环绕通知非常强大,可以决定目标方法是否执行,什么时候执行,执行时是否需要替换方法参数,执行完毕是否需要替换返回值。 * 环绕通知第一个参数必须是org.aspectj.lang.ProceedingJoinPoint类型 */ @Around("executeService()") public Object doAroundAdvice(ProceedingJoinPoint proceedingJoinPoint){ System.out.println("环绕通知的目标方法名:"+proceedingJoinPoint.getSignature().getName()); try { Object obj = proceedingJoinPoint.proceed(); return obj; } catch (Throwable throwable) { throwable.printStackTrace(); } return null; }
三、AOP配置项
打开spring-boot-autoconfigure-1.5.9.RELEASE.jar查看包org.springframework.boot.autoconfigure.aop。AopAutoConfiguration
配置项:spring.aop.auto
@ConditionalOnProperty(prefix = "spring.aop", name = "auto", havingValue = "true", matchIfMissing = true)
spring.aop.auto=true【默认】含义:启用aop
配置项:spring.aop.auto.proxy-target-class
@ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = true)
spring.aop.auto.proxy-target-class=false【默认】,含义:启用JdkDynamicAutoProxyConfiguration动态代理,代理接口,不能代理类
注意如果代理的是类会自动切换至CglibAutoProxyConfiguration代理
spring.aop.auto.proxy-target-class=true,含义:启用CglibAutoProxyConfiguration动态代理,代理接口和类
四、配置参数
1、默认也提供了:@EnableAspectJAutoProxy注解
有两个参数:代理方式:proxyTargetClass 默认false,与上文一致
exposeProxy:获取代理后对象,默认false。不支持。如果是true,可以通过AopContext.currentProxy()获取
2、后置注解增加参数
@Before("execution (* com.lhx.spring.springboot.dao..*.*(..))") public void logAfter(JoinPoint point) { System.out.println("after log done," + point.getTarget().getClass() + ",args=" + Arrays.asList(point.getArgs()) +",method="+point.getSignature()); }