SpringAOP

文章目录

AOP思想

​ AOP:面向切面编程,核心思想就是解耦。将每个方法/类中通用的逻辑剥离出来,封装成一个独立的模块,在程序运行期间自动将这些逻辑加入到业务代码中(织入)。从而实现业务逻辑与非业务逻辑之间的解耦

非业务逻辑包括:日志记录,性能统计,权限校验等一些与业务无关,但每个方法中都包含的逻辑

AOP可以做到在程序运行期间在不修改源代码的基础上对原有方法进行增强

SpringAOP

  1. 切面:封装通用的非业务逻辑的类
  2. 连接点:程序运行中所有可以插入切面的方法
  3. 切入点:实际被拦截的连接点
  4. 通知/增强:切面插入具体的方法后,所执行的逻辑
  5. 目标对象:被一个或多个切面所拦截和增强的,包含业务逻辑的原始对象
  6. 织入:将切面应用到目标对象,并创建代理对象

通知类型

  1. @Before:目标方法执行前执行

    除非这个通知抛出异常,否则不能阻止目标方法的执行

  2. @AfterReturning:目标方法执行完成并返回结果后执行执行

    目标方法内部抛出异常,这个通知不会执行

  3. @AfterThrowing:目标方法运行过程中,抛出异常时执行

    只有发生异常时才能触发,可以指定只拦截特定类型的对象

  4. @After:目标方法执行完成并返回结果后执行执行

    目标方法内部无论是否抛出异常,这个通知都会执行

  5. @Around:目标方法前后都会执行

    java 复制代码
    	@Around("execution(...)")
    										//只有@Around有这个参数
    public Object timeRecord(ProceedingJoinPoint proceedingJoinPoint){
    			//方法执行前...
       Object proceed=null;
            try{
               //执行目标方法,并接收返回值
               proceed= proceedingJoinPoint.proceed();
            }catch (Throwable throwable){
               //抛出异常时执行的逻辑...
            }
     			//方法执行后...
    	return proceed;
    }

注意:

  • @Around方法的返回值必须为Object,用于返回原始方法的返回值,否则原始方法执行完毕今后无法获取返回值。
  • 其他四种通知虽然不能接收ProceedingJoinPoint但是都可以接收一个JoinPoint参数,来获取目标方法的签名、方法名、参数列表等基本信息

执行顺序

@PointCut

将公共的切点表达式提取出来,用到时直接引用

java 复制代码
@Pointcut("execution(...)")
	public void pt(){} 

@Before("pt()")
...

引用其他切面类中的切点定义时,使用全限定类名.方法()名

切面优先级@Order

使用@Order控制不同切面的执行顺序,@Order接收一个整数,值越小,优先级越高。

最高优先级:Ordered.HIGHEST_PRECEDENCE

最低优先级:Ordered.LOWEST_PRECEDENCE

优先级越高,目标方法执行前越先执行,同时目标方法执行后,越后执行

切点表达式

execution()

复制代码
execution([权限修饰符] 返回值类型 [包名.类名.]方法名(参数类型) [抛出异常类型])
// execution(* com.example.demo..*(..))
  1. *:匹配任意单个元素,任意返回值/类名/方法名
  2. ..
    1. 包路径中:当前包及其所有子包
    2. 参数列表中:任意数量,任意类型的参数
  3. +:放在接口或类名后,表示匹配该类及其所有子类或实现类

@annotation

使用自定义注解+@annotation来描述一些无规则的切入点

  1. 编写自定义注解

    java 复制代码
    //表示为方法注解
    @Target(ElementType.METHOD)
    //注解的生命周期
    @Retention(RetentionPolicy.RUNTIME)
    public @interface MyAspect { }

    @Target常用取值

    ElementType.TYPE: 用于描述类、接口(包括注解类型) 或enum声明 ElementType.METHOD: 描述方法

    ElementType.PARAMETER: 描述参数

    ElementType.TYPE_USE: 可以标注任意类型

    @Retention描述注解的生命周期

    1. RetentionPolicy.SOURCE:表示注解仅存在于源代码中, 编译成字节码后会被丢弃(@Data)
    2. RetentionPolicy.CLASS:编译时注解. 表示注解存在于源代码和字节码中, 但在运行时会被丢弃
    3. RetentionPolicy.RUNTIME:运行时注解. 表示注解存在于源代码, 字节码和运行时中
  2. 使用@annotation切点表达式定义切点,只对@MyAspect生效

    java 复制代码
    @Before("@annotation(org.en.aop.aspect.MyAspect)")
        public void before(){
            log.info("method before-myaspect");
        }
  3. 在需要的方法上使用@MyAspect添加自定义注解

AOP同类内部方法调用失效

在同一个类中,方法A调用带有AOP注解的方法B,B的切面逻辑不会生效。因为是直接调用的目标对象本身,并没有经过代理对象

代理模式

提供一个代理类,在调用方法时不再直接对目标方法进行调用,而是通过调用代理类进行间接调用

  1. Subject: 业务接口类,抽象类或者接口,用于定义代理类的逻辑
  2. RealSubject: 业务实现类. 具体的业务执行, 也就是被代理对象.
  3. Proxy: 代理类. RealSubject的代理.

静态代理

程序运行之前,代理类的.class文件就已经存在

动态代理

不需要针对每个目标对象都创建一个代理对象,将创建代理对象的工作推迟到程序运行时由JVM实现。代理类是在程序运行时动态生成的


相关推荐
数聚天成DeepSData6 分钟前
数聚天成 DeepSData 数据价值落地实战指南
java·maven·devops
techdashen7 分钟前
Go设计取舍之四: map不变时能否并发修改不同value
开发语言·后端·golang
小小小米粒15 分钟前
阿姆达尔定律(Amdahl‘s Law)
java·开发语言
知彼解己16 分钟前
Java 版本演进
java·开发语言·spring boot
极客先躯32 分钟前
高级java每日一道面试题-2026年05月11日-实战篇[Docker]-如何容器化金融产品推荐系统?
java·运维·docker·容器·金融·高级面试·金融产品推荐系统
朱容zr3331331 小时前
为什么推荐使用自增主键?使用UUID作为主键的优缺点是什么?
java·运维·数据库·后端·mysql·面试·性能优化
用户608186527901 小时前
一文吃透 Avalonia 布局容器|Grid 到 UniformGrid 核心用法 + 语法糖实战
后端
Ai拆代码的曹操1 小时前
Dubbo 线程池爆满排坑:Linux 用户线程数限制,一个容易被忽略的根因
后端·源码阅读
艾莉丝努力练剑1 小时前
【Linux:动静态库】Linux 动静态库与可执行文件
linux·运维·服务器·学习·面试·文件系统·动静态库
知彼解己1 小时前
Eclipse Temurin:企业级 Java JDK 发行版的最佳实践
java·ide·eclipse