一:什么是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
人们
高估了一天能做的事情
低估了一年能做的事情
来源:https://www.cnblogs.com/fcml/p/10581186.html