Spring-AOP

切面类:有方法开始、方法返回、方法异常、方法结束。(通知方法)

~切入点: 执行通知方法的叫切入点,通过切入点表达式------>选择有需要的切入点。

作用:切面类定义的通知方法动态发现目标对象执行哪个位置,动态插入目标对象中。

编写通知方法,告诉spring一下通知方法何时何地运行(切入点表达式)。

~连接点:封装了当前目标对象详细信息,在参数使用JoinPoint。

切入点

一、何时?

@Before方法执行之前运行

@AfterReturning方法执行正常返回结果运行

@AfterThrowing方法抛出异常运行

@After方法执行之后运行

@Around环绕通知

二、何地?

切入点表达式:

1)execution(方法的全签名)

1.全写法:public int com.scoro.spring.aop.calculator.MathCalculator.add(int,int)

2.省略写法:int add(int ,int)

通配符:

..; ①任意多个参数和类型(参数位置)②代表多个层级(类型位置)

*;代表所有(任意字符)

2)args(int,int)------>以指定参数切入

3)@annotation("注解全类名")------>标了注解的切(方法上有没有标注解)

java 复制代码
@Component
public class MathCalculatorImpl implements MathCalculator {
    @Override
    public int add(int a, int b) {
        int sun=a+b;
        System.out.println("sum="+sun);
        return sun;
    }
    @Override
    public int subtract(int a, int b) {
        int sun=a-b;
        System.out.println("sum="+sun);
        return sun;
    }
    @Override
    public int multiply(int a, int b) {
        int sun=a*b;
        System.out.println("sum="+sun);
        return sun;
    }
    @Override
    public int divide(int a, int b) {
        int sun=a/b;
        System.out.println("sum="+sun);
        return sun;
    }

    @MyAn
    @Override
    public void annMethod(int a, int b) {
        System.out.println("annotation"+a);
    }
}
java 复制代码
@Component
@Aspect //告诉spring这个组件是切面
public class LogAspect {
//    方法执行之前运行
    @Before("execution(int com.scoro.spring.aop.calculator.MathCalculator.*(..))")
    public void logBefore() {
        System.out.println("切面~开始");
    }

//    方法执行正常返回结果运行
    @AfterReturning("execution(int *(int,int))")
    public void logReturn() {
        System.out.println("切面~返回");
    }

//    方法抛出异常运行
    @AfterThrowing("execution(int *(int,int))")
    public void logException() {
        System.out.println("切面~异常");
    }

//    方法执行之后运行
    @After("execution(int *(int,int))")
    public void logAfter() {
        System.out.println("切面~结束");
    }

    @Before("args(int,int)")  //args:以指定参数切
    public void arg(){
        System.out.println("切面~parameter");
    }

    @Before("@annotation(com.scoro.spring.aop.annotation.MyAn)")  //@annotation注解:标了注解的切(方法上有没有标注解)
    public void annoMethod(){
        System.out.println("切面~annotation");
    }
}
java 复制代码
@Target({ ElementType.PARAMETER,ElementType.METHOD}) //说明注解的作用目标,里面value值按需求添加
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyAn {

}

@Pointcut 简化开发

java 复制代码
@Component
@Aspect //告诉spring这个组件是切面
public class LogAspect {

    @Pointcut("execution(int com.scoro.spring.aop.calculator.MathCalculator.*(..))")
    public void pointCut(){} //空方法是为了给被人引用

    @Before("pointCut()")
    public void logBefore() {
        System.out.println("切面~开始");
    }

    @AfterReturning(value = "pointCut()")
    public void logReturn() {
        System.out.println("切面~返回");
    }
}

@Around环绕通知

环绕通知:①整合了前面四种通知方法

②可以控制目标方法是否执行,修改目标方法参数等执行结果。

java 复制代码
@Aspect
@Component
public class AroundAspect {
    @Pointcut("execution(int com.scoro.spring.aop.calculator.MathCalculator.*(..))")
    public void pointCut(){}
    /**
     * 环绕通知固定写法:
     * Object:返回值
     * ProceedingJoinPoint:可以继续推进的切点
     */

    @Around("pointCut()")
    public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
//        获取目标参数
        Object[] args = proceedingJoinPoint.getArgs();
//        前置
        System.out.println("环绕~前置");
        Object proceed=null;
        try {
            //    接受传参数的proceed,在传参数前实现修改目标方法执行用的参数
            args[1]=9;  //修改参数
            proceed = proceedingJoinPoint.proceed(args);//继续执行目标方法
            System.out.println("环绕~返回"+proceed);
        }catch (Exception e){
            System.out.println("环绕~异常");
            throw e; //吃掉异常,抛出异常
        }finally {
            System.out.println("环绕~后置");
        }
//        修改返回值
        return proceed;
    }
}

连接点

JoinPoint 连接点:获取当前目标方法的详细信息

java 复制代码
@Component
@Aspect
public class LogAspect {
    @Before("execution(int com.scoro.spring.aop.calculator.MathCalculator.*(..))")
    public void logBefore(JoinPoint joinPoint) {
 //       拿到方法全签名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
//        方法名
        String name = signature.getName();
//        目标方法传过来的参数值
        Object[] args = joinPoint.getArgs();
        System.out.println(name+"切面~开始"+ Arrays.toString(args));
    }

    @AfterReturning(value = "execution(int *(int,int))",returning = "result") //returning = "result"获取目标方法返回值
    public void logReturn(JoinPoint joinPoint,Object result) {
 //      获取目标方法返回值
        System.out.println("切面~返回"+result);
    }

    @AfterThrowing(value = "execution(int *(int,int))",throwing = "e")
    public void logException(JoinPoint joinPoint,Exception e) {
        System.out.println("切面~异常"+e);
    }
}

多切面执行顺序

例:有两个切面对MathCalculatorImpl类进行干预,两个切面类是谁先干预?

相关推荐
三小河4 分钟前
Agent Skill与Rules的区别——以Cursor为例
前端·javascript·后端
kali-Myon8 分钟前
2025春秋杯网络安全联赛冬季赛-day1
java·sql·安全·web安全·ai·php·web
我是咸鱼不闲呀11 分钟前
力扣Hot100系列20(Java)——[动态规划]总结(下)( 单词拆分,最大递增子序列,乘积最大子数组 ,分割等和子集,最长有效括号)
java·leetcode·动态规划
三小河17 分钟前
前端视角详解 Agent Skill
前端·javascript·后端
清水白石00820 分钟前
深入解析 LRU 缓存:从 `@lru_cache` 到手动实现的完整指南
java·python·spring·缓存
牛奔26 分钟前
Go 是如何做抢占式调度的?
开发语言·后端·golang
颜酱30 分钟前
二叉树遍历思维实战
javascript·后端·算法
符哥200833 分钟前
C++ 进阶知识点整理
java·开发语言·jvm
Sayuanni%31 小时前
初阶_多线程1(线程含义与关键属性)
java
程序媛徐师姐1 小时前
Java基于微信小程序的模拟考试系统,附源码+文档说明
java·微信小程序·java模拟考试系统小程序·模拟考试微信小程序·模拟考试系统小程序·模拟考试小程序·java模拟考试小程序