Spring aop 注解

5 阅读4分钟

案例

  1. 加入依赖
<!-- spring-aspects会帮我们传递过来aspectjweaver -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>6.0.6</version>
</dependency>

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>6.0.6</version>
</dependency>
  1. 接口定义
// + - * / 运算的标准接口

public interface Calculator {
    int add(int i, int j);
    int sub(int i, int j);
    int mul(int i, int j);
    int div(int i, int j);
}
  1. 实现纯净实现类,并且加入到 IOC 容器中
import org.springframework.stereotype.Component;
// 实现计算接口,单纯添加 + - * / 实现
@Component
public class CalculatorPureImpl implements Calculator {
    @Override
    public int add(int i, int j) {
        int result = i + j;
        return result;
    }

    @Override
    public int sub(int i, int j) {
        int result = i - j;
        return result;
    }

    @Override
    public int mul(int i, int j) {
        int result = i * j;
        return result;
    }

    @Override
    public int div(int i, int j) {
        int result = i / j;
        return result;
    }
}
  1. 现在需要给类的方法中注入日志 LogAdvice,声明切面类
/**
 *  日志输出增强类
 *  1. 定义方法存储增强代码:具体定义几个方法,根据插入的位置决定!
 *  2. 使用注解配置,指定插入目标方法的位置
 *      前置 @Before
 *      后置 @AfterReturning
 *      异常 @AfterThrowing
 *      最后 @After
 *      环绕 @Around
 *  3. 配置切点表达式,选中插入的方法
 *  4. 增强也要放到 IOC 容器,也需要标注是一个切面
 *  5. @Aspect 声明当前类为一个 AOP 切面,里面包含切点和通知,需要参与 AOP 代理逻辑
 */
@Component
@Aspect
public class LogAdvice {
    // 切点 + 增强方法 = 切面
    @Before("execution(* com.study.java.service.*.*(..))") // 切点
    public void start(){
        // 增强方法
        System.out.println("方法开始了");
    }
    @After("execution(* com.study.java.service.*.*(..))")
    public void after(){
        // 增强方法
        System.out.println("方法结束了");
    }
    @AfterThrowing(pointcut = "execution(* com.study.java.service.*.*(..))", throwing = "ex")
    public void error(Exception ex) {
        System.out.println("方法报错了:" + ex.getMessage());
    }
}
  1. 开启 Spring 配置类、扫描指定包、开启 Spring AOP 对 AspectJ 注解的支持
@Configuration
@ComponentScan(basePackages = "com.study.java")
// 如果没有这个注解,@Aspect 不会生效
@EnableAspectJAutoProxy
public class MyConfig {}
  1. 想知道目标函数的具体信息(函数名称、参数值、返回值、异常对象 等等)
@Component
@Aspect
public class LogAdvice {
    @Before(value = "execution(public int com.study.java.service.Calculator.add(int,int))")
    public void printLogBeforeCore(JoinPoint joinPoint) {
        // 1.JoinPoint 包含目标方法的信息
        // 方法的签名:一个方法的全部声明信息
        Signature signature = joinPoint.getSignature();
        // 2.通过方法的签名对象获取目标方法的详细信息
        String methodName = signature.getName();
        System.out.println("methodName = " + methodName);

        int modifiers = signature.getModifiers();
        System.out.println("modifiers = " + modifiers);

        String declaringTypeName = signature.getDeclaringTypeName();
        System.out.println("declaringTypeName = " + declaringTypeName);

        // 3.通过JoinPoint对象获取外界调用目标方法时传入的实参列表
        Object[] args = joinPoint.getArgs();

        // 4.由于数组直接打印看不到具体数据,所以转换为List集合
        List<Object> argList = Arrays.asList(args);

        System.out.println("[AOP前置通知] " + methodName + "方法开始了,参数列表:" + argList);
    }

    @AfterReturning(value = "execution(public int com.study.java.service.Calculator.add(int,int))", returning = "targetMethodReturnValue") // 指定用哪个参数接收返回结果
    public void printLogAfterCoreSuccess(JoinPoint joinPoint, Object targetMethodReturnValue) {
        // 在 targetMethodReturnValue 中接返回结果
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[AOP返回通知] " + methodName + "方法成功结束了,返回值是:" + targetMethodReturnValue);
    }


    @AfterThrowing(value = "execution(public int com.study.java.service.Calculator.add(int,int))", throwing = "targetMethodException")
    public void printLogAfterCoreException(JoinPoint joinPoint, Throwable targetMethodException) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[AOP异常通知] "+methodName+"方法抛异常了,异常类型是:" + targetMethodException.getClass().getName());
    }
}
  1. 重用切点表达式
    上面的案例中多次使用了:execution(public int com.study.java.service.Calculator.add(int,int)),需要抽离出来
@Component
@Aspect
public class LogAdvice {
    @Pointcut("execution(public int com.study.java.service.Calculator.add(int,int))")
    public void pointCut() {}

    @Before(value = "pointCut()")
    public void printLogBeforeCore(JoinPoint joinPoint) {
        // 1.JoinPoint 包含目标方法的信息
        // 方法的签名:一个方法的全部声明信息
        Signature signature = joinPoint.getSignature();
        // 2.通过方法的签名对象获取目标方法的详细信息
        String methodName = signature.getName();
        System.out.println("methodName = " + methodName);

        int modifiers = signature.getModifiers();
        System.out.println("modifiers = " + modifiers);

        String declaringTypeName = signature.getDeclaringTypeName();
        System.out.println("declaringTypeName = " + declaringTypeName);

        // 3.通过JoinPoint对象获取外界调用目标方法时传入的实参列表
        Object[] args = joinPoint.getArgs();

        // 4.由于数组直接打印看不到具体数据,所以转换为List集合
        List<Object> argList = Arrays.asList(args);

        System.out.println("[AOP前置通知] " + methodName + "方法开始了,参数列表:" + argList);
    }

    @AfterReturning(value = "pointCut()", returning = "targetMethodReturnValue") // 指定用哪个参数接收返回结果
    public void printLogAfterCoreSuccess(JoinPoint joinPoint, Object targetMethodReturnValue) {
        // 在 targetMethodReturnValue 中接返回结果
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[AOP返回通知] " + methodName + "方法成功结束了,返回值是:" + targetMethodReturnValue);
    }


    @AfterThrowing(value = "pointCut()", throwing = "targetMethodException")
    public void printLogAfterCoreException(JoinPoint joinPoint, Throwable targetMethodException) {
        String methodName = joinPoint.getSignature().getName();
        System.out.println("[AOP异常通知] "+methodName+"方法抛异常了,异常类型是:" + targetMethodException.getClass().getName());
    }
}

或者彻底提取出去,放在一个类中维护

  • MyPointCut.java
@Component
public class MyPointCut {
    @Pointcut("execution(public int com.study.java.service.Calculator.add(int,int))")
    public void pointCut() {}
}
@Component
@Aspect
public class LogAdvice {

    @Before(value = "com.study.java.service.MyPointCut.pointCut()")
    public void printLogBeforeCore(JoinPoint joinPoint) {
        ...
    }

    @AfterReturning(value = "com.study.java.service.MyPointCut.pointCut()", returning = "targetMethodReturnValue") // 指定用哪个参数接收返回结果
    public void printLogAfterCoreSuccess(JoinPoint joinPoint, Object targetMethodReturnValue) {
        ...
    }


    @AfterThrowing(value = "com.study.java.service.MyPointCut.pointCut()", throwing = "targetMethodException")
    public void printLogAfterCoreException(JoinPoint joinPoint, Throwable targetMethodException) {
        ...
    }
}
  1. 控制某一个 增强优先级 先执行 image.png
  • @Order(较小的数):优先级高
  • @Order(较大的数):优先级低
@Component
@Aspect
@Order(10)
public class TxAdvice {}
@Component
@Aspect
@Order(20)
public class LogAdvice {}