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类进行干预,两个切面类是谁先干预?

相关推荐
卷无止境7 分钟前
Python 异常处理:从入门到工程实践
后端·python
AINative软件工程10 分钟前
LLM 应用的熔断降级工程实践:Circuit Breaker 不只是重试的升级版
后端·llm·ai编程
学计算机的计算基10 分钟前
操作系统内存管理全解:虚拟内存、页表、COW、malloc、OOM一篇搞定
java·笔记·算法
tachibana215 分钟前
hot100 前 K 个高频元素(347)
java·数据结构·算法·leetcode
程序员爱钓鱼24 分钟前
Go 开发环境安装(Windows、macOS、Linux)
后端·面试·go
程序员爱钓鱼30 分钟前
Rust String 与 &str 详解:字符串所有权、借用与转换
前端·后端·rust
万亿少女的梦16833 分钟前
基于Spring Boot、Java与MySQL的网络订餐系统设计与实现
java·spring boot·mysql·系统设计·网络订餐
咩咩啃树皮9 小时前
第40篇:Vue3组件化开发精讲——组件拆分、复用、父子通信、工程化架构
java·前端·架构
阳光是sunny9 小时前
LangGraph中的Reducer是什么
前端·人工智能·后端
灯澜忆梦9 小时前
GO_并发编程---定时器
开发语言·后端·golang