SpringAop基础知识
一:什么是AOP?
AOP(Aspect Oriented Programming)被称为面向切面编程。
连接点:就是一个个方法,通俗来说,所有的方法都是连接点。(Spring AOP 只能使IOC容器中的Bean的方法)
目标对象(Target):就是那些即将切入切面的对象,也就是那些被通知的对象。
代理对象(Proxy):将通知应用到目标对象之后被动态创建的对象。可以简单地理解为,代理对象的功能等于目标对象的核心业务逻辑功能加上共有功能。代理对象对于使用者而言是透明的,是程序运行过程中的产物。
三、SpringAop的五种通知类型
1、前置通知(Before Advice): 在连接点之前执行的Advice,不过除非它抛出异常,否则没有能力中断执行流。使用 @Before 注解使用这个Advice。
@Before(value = "point()") public void before(JoinPoint joinPoint) { log.info("前置通知..."); //获取目标方法的参数信息 Object[] objects = joinPoint.getArgs(); //AOP代理类的信息 Object proxy = joinPoint.getThis(); //代理的目标对象 Object target = joinPoint.getTarget(); //通知的签名 Signature signature = joinPoint.getSignature(); //代理的方法名称 String methodName = signature.getName(); //AOP代理类的名字 String proxyClassName = signature.getDeclaringTypeName(); //AOP代理类的类(class)信息 Class proxyClass = signature.getDeclaringType(); //获取RequestAttributes RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); //从获取RequestAttributes中获取HttpServletRequest的信息(注意这里获取的是request域的信息) HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST); //从获取RequestAttributes中获取HttpSession的信息(注意这里获取的是session域的信息) HttpSession session = (HttpSession) requestAttributes.resolveReference(RequestAttributes.REFERENCE_SESSION); //获取请求参数 Enumeration<String> enumeration = request.getParameterNames(); Map<String, String> parameterMap = new HashMap<>(); //保存了请求参数信息 while (enumeration.hasMoreElements()) { String parameter = enumeration.nextElement(); parameterMap.put(parameter, request.getParameter(parameter)); } }
2、返回之后通知(After Retuning Advice): 在连接点正常结束之后执行的Advice。例如,如果一个方法没有抛出异常正常返回。通过@AfterReturning 关注使用它。
/** * 如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息 */ @AfterReturning(value = "point()", returning = "keys") public void afterReturn(JoinPoint joinPoint, Object keys) { log.info("后置返回通知..."); }
/** * 如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数 */ @AfterReturning(value = "point()", returning = "keys") public void afterReturn(Object keys) { log.info("后置返回通知. 返回值为:" + keys); }
另外,对于后置返回通知,通知方法的参数类型必须与代理目标方法的返回类型一致,否则后置返回通知不生效。当通知方法的入参为Object类型时将匹配任何目标返回值。
3、抛出(异常)后执行通知(After Throwing Advice): 如果一个方法通过抛出异常来退出的话,这个Advice就会被执行。通用 @AfterThrowing 注解来使用。
@AfterThrowing(value = "point()", throwing = "exception") public void afterThrow(JoinPoint joinPoint, Throwable exception) { if (exception instanceof NullPointerException) { log.info("发生了空指针异常!!!"); } exception.printStackTrace(); }
4、后置通知(After Advice): 无论连接点是通过什么方式退出的(正常返回或者抛出异常)都会执行在结束后执行这些Advice。通过 @After 注解使用。
@After(value = "point()") public void after(JoinPoint joinPoint) { log.info("后置最终通知..."); }
5、围绕通知(Around Advice): 围绕连接点执行的Advice,就你一个方法调用。这是最强大的Advice。通过 @Around 注解使用。
@Around(value = "point()") public Object logMethodExecuteTime(ProceedingJoinPoint joinPoint) { Object object = null; long start = System.currentTimeMillis(); //joinPoint.proceed();执行前等于前置通知 try { object = joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); //异常通知 } String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); log.info("execute " + className + ":" + methodName + " spend " + (System.currentTimeMillis() - start) + "ms."); //后置通知... return object; }
四、利用SpringAop记录方法执行时长
1、本文使用自定义注解实现切点表达式,因此首先创建一个注解以及切点表达式。
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ExecuteTime { }
@Pointcut(value = "@annotation(com.fcml.study.transaction.propagation.aop.annotation.ExecuteTime)") public void point() { }
2、定义记录方法执行时长的切面逻辑
@Around(value = "point()") public Object logMethodExecuteTime(ProceedingJoinPoint joinPoint) { Object object = null; long start = System.currentTimeMillis(); try { object = joinPoint.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } String className = joinPoint.getTarget().getClass().getName(); String methodName = joinPoint.getSignature().getName(); log.info("execute " + className + ":" + methodName + " spend " + (System.currentTimeMillis() - start) + "ms."); return object; }
3、在需要记录的方法上加上注解即可。
github: https://github.com/dwkfcml/Code-Fragment/tree/master/Aop/first-aop
人们
高估了一天能做的事情
低估了一年能做的事情