AOP是通过动态代理实现的,动态代理又分为两个部分:JDK动态代理 和 CGLIB动态代理,以下两点需要记住:
1、AOP基于动态代理模式。
2、AOP是 方法级别 的。即,在方法执行的前,后,执行过程中,报错,返回结果等等流程中进行特殊操作
maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
这里可以不用指定版本号, 在SpringBoot 的 parent 包里面有版本的控制。
示例
- 可以代理指定包中的方法 通过 execution 表达式
- 可以代理方法上加了注解的
import java.lang.annotation.*;
/**
* desc: 自定义注解
*
* @author qts
* @date 2022/4/21 0021
*/
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface MyAnnotation {
String value() default "";
String myValue() default "我是自定义注解";
}
import lombok.extern.slf4j.Slf4j;
import org.aopalliance.intercept.Joinpoint;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
/**
* desc: 测试 aop
*
* @author qts
* @date 2022/4/21 0021
*/
@Aspect
@Component
@Slf4j
public class MyAop {
@Pointcut("@annotation(com.intellsite.business.util.MyAnnotation)")
public void testMyAnnotationAspect() {}
/**
* 前置通知,目标方法调用前被调用
* 除@Around外,每个方法里都可以加或者不加参数JoinPoint。
* JoinPoint包含了类名、被切面的方法名、参数等属性。
* @annotation中的值,需要和方法参数名相同
**/
//@Before(value = "testMyAnnotationAspect()") // 注解无参数 写法
@Before(value = "testMyAnnotationAspect() && @annotation(myAnnotation)") // 注解带参数 写法
public void before(JoinPoint joinpoint,MyAnnotation myAnnotation) {
log.info("AOP before 执行 ...: 参数类型 {},name:{}",joinpoint.getArgs(),joinpoint.getSignature().getName());
log.info("before-value ==" + myAnnotation.value() + ", before-myValue == " + myAnnotation.myValue());
// 接收到请求,记录请求内容
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 记录请求内容
System.out.println("URL : " + request.getRequestURL().toString());
System.out.println("HTTP_METHOD : " + request.getMethod());
System.out.println("IP : " + request.getRemoteAddr());
System.out.println("CLASS_METHOD : " + joinpoint.getSignature().getDeclaringTypeName() + "." + joinpoint.getSignature().getName());
System.out.println("ARGS : " + Arrays.toString(joinpoint.getArgs()));
}
/**
* 前置通知,目标方法调用前被调用
* 除@Around外,每个方法里都可以加或者不加参数JoinPoint。
* JoinPoint包含了类名、被切面的方法名、参数等属性。
* @annotation中的值,需要和方法参数名相同
**/
@Around(value = "testMyAnnotationAspect() && @annotation(myAnnotation))")
public Object around(ProceedingJoinPoint proceedingJoinPoint, MyAnnotation myAnnotation) {
try {
log.info("AOP around 开始... 执行方法 ... : [ {} ], 参数为: [ {} ]",proceedingJoinPoint.getSignature().getName(),proceedingJoinPoint.getArgs());
log.info("自定义注解值 value: " + myAnnotation.value() + "myValue: " + myAnnotation.myValue());
Object proceed = proceedingJoinPoint.proceed();
log.info("AOP around 结束... 执行方法 ... : [ {} ], 参数为: [ {} ]",proceedingJoinPoint.getSignature().getName(),proceedingJoinPoint.getArgs());
return proceed;
}catch (Throwable throwable) {
log.info("AOP around 执行错误... error :{}", throwable.getMessage());
throwable.printStackTrace();
return "执行around出错。。。";
}
}
/**
* 最终通知,目标方法执行完之后执行
**/
@After(value = "execution(* com.intellsite.business.controller.AnnotationController.*(..))")
public void after(JoinPoint joinPoint) {
log.info("AOP after 执行... : {}",joinPoint.toLongString());
}
/**
* 后置返回通知
* 如果参数中的第一个参数为JoinPoint,则第二个参数为返回值的信息
* 如果参数中的第一个参数不为JoinPoint,则第一个参数为returning中对应的参数
* returning 只有目标方法返回值与通知方法具有相应参数类型时才能执行后置返回通知,否则不执行
* 除了使用上面定义好的切面aspect(), 也可以直接使用表达式。
**/
@AfterReturning(value = "execution(* com.intellsite.business.controller.AnnotationController.getInfo(..)))",returning = "result")
public void afterReturning(JoinPoint joinPoint,Object result) {
log.info("AOP afterReturning 执行... :返回结果:{}", result);
}
/**
* 后置异常通知
* 定义一个名字,该名字用于匹配通知实现方法的一个参数名,当目标方法抛出异常返回后,将把目标方法抛出的异常传给通知方法;
* throwing 只有目标方法抛出的异常与通知方法相应参数异常类型时才能执行后置异常通知,否则不执行,
**/
@AfterThrowing(value = "testMyAnnotationAspect()",throwing = "exception")
public void afterThrowing(JoinPoint joinPoint, Throwable exception) {
log.error("AOP afterThrowing 执行... , msg : {}", exception.getMessage());
if (exception instanceof NullPointerException) {
log.info("空指针异常");
}
}
}
- 没抛异常的顺序
正常: around开始 --》 before 执行–》afterReturning 执行–》after 执行–》around结束 - 异常时执行顺序
异常: around开始–》before 执行–》afterThrowing 执行–》after 执行–》around