Spring Aop

一、AOP(面向切面编程)

切面是指某一个特定的问题,AOP就是面向特定的方法编程,例如登录拦截器,统一数据返回,统一异常处理,都是使用AOP思想。AOP是Spring框架的第二大核心。

二、Spring AOP也是AOP的一种实现

编写AOP程序,记录Controller每个方法执行的时间

切点(Pointcut):

切点就是一组规则告诉程序哪些方法进行功能加强

切点表达式(例如代码中):

连接点:

满足切点表达式的方法就是连接点

路径下的方法都是连接点

通知( Advice)

通知就是具体要做的工作,即共同重复的逻辑,例如记录多个方法的耗时

切面:(切点+通知)

即:

切面类:

切面所在的类,即被@Aspect修饰的类

通知类型

  1. @Before 前置通知

执行时机:目标方法执行之前运行

  1. @AfterReturning 后置返回通知

执行时机:目标方法正常执行完毕、成功返回结果后,异常不执行

  1. @AfterThrowing 异常通知

执行时机:目标方法抛出异常时执行

  1. @After 最终通知

执行时机:无论目标方法是正常结束还是抛出异常,该通知都会在最后执行。

  1. @Around 环绕通知(功能最强)

执行时机:包裹目标方法,手动控制方法执行

java 复制代码
package com.bite.springaopdemo.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Aspect
@Component
@Slf4j

public class AspectDemo1 {
    //添加环绕通知
    @Around("execution(* com.bite.springaopdemo.controller.*.*(..))")
    public Object recordTime(ProceedingJoinPoint pjp)throws  Throwable{//pjp指的是连接点对象
        //记录方法开始时间
        long begin=System.currentTimeMillis();
        log.info("Around方法开始执行");
        //执行原始方法
        Object result=pjp.proceed();
        //记录方法执行结束时间
        long end=System.currentTimeMillis();
        log.info(pjp.getSignature()+"执行耗时{}ms",end-begin);//getSignature()被拦截对象的类名方法名
        log.info("Around方法执行结束");
        return result;
    }
    //前置通知
    @Before("execution(* com.bite.springaopdemo.controller.*.*(..))")
    public   void  doBefore(){
        log.info("执行before方法");
    }
    //后置通知
    @After("execution(* com.bite.springaopdemo.controller.*.*(..))")
    public   void  doAfter(){
        log.info("执行after方法");
    }
//    //返回后通知
    @AfterReturning("execution(* com.bite.springaopdemo.controller.*.*(..))")
    public   void  doAfterReturning(){
        log.info("执行afterReturning方法");
    }
    //抛出异常后通知
    @AfterThrowing("execution(* com.bite.springaopdemo.controller.*.*(..))")
    public  void  doafterThrowing(){
        log.info("执行AfterThrowing方法");
    }

}

测试接口:

java 复制代码
package com.bite.springaopdemo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/test")
public class TestController {
@RequestMapping("/t1")
public Integer t1(){
System.out.println("执行方法");
return 1;
}
}

出现异常时候:

@afterReturning标识方法不会执行,@AfterThrowing标识的通知执行

@Around环绕方法有异常的时候,环绕后的代码不会在执行了

@PointCut将公共代码提取出来

没有规定切面类的优先级的时候:

1、@Before(目标方法执行之前)

多个切面的前置通知,按照切面类名称字典顺序执行: Demo1 排在前面 → Demo2 → Demo3,越靠后的切面,Before 越晚执行。

2、@After(目标方法执行之后,最终通知)

Spring AOP 采用栈结构执行:先进后出。 想象成套盒子: 先套上 Demo1 盒子 → 再套 Demo2 盒子 → 最后套 Demo3 盒子; 执行完目标方法之后,拆盒子要从最外层开始拆: 先拆掉最外面的 Demo3 → 再拆 Demo2 → 最后拆 Demo1。

所以: Before 入栈顺序:1 → 2 → 3 After 出栈顺序:3 → 2 → 1

可以使用注解:@Order(优先级数值) 数值越小,优先级越高,执行越早。

切点表达式(具体)

1.execution

execution(访问修饰符? 返回值类型 包路径.类名.方法名(参数列表) 异常抛出?)

复制代码
@Pointcut("execution(* com.bite.springaopdemo.controller.*.*(..))")
  • * 匹配任意单个内容
  • .. 匹配多层包 / 任意个参数

2.annotation

1.创建有一个注解类:

2.切面类:(定义切点只对@MyAspect生效)

3.添加自定义注解

4.测试结果

Spring AOP实现方式:

1.基于注解@Aspect

2.基于自定义注解

3.基于Spring API(通过xml配置的方式)

4.基于代理实现

三、Spring AOP原理:

基于动态代理实现的,分为

1.代理模式

提供一个代理类,让我们调用目标方法的时候可以通过目标方法简介调用
根据代理创建的时期分为静态代理和动态代理:

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

**动态代理:**不需要为每个对象目标创建一个代理对象,而是把代理对象的工作推迟到程序运行时,由JVM实现,即"业务来了再创建"

java对动态代理进行了实现,常见实现方式时JDK动态代理CGLIB动态代理

CGLIB和JDK区别:

1.CGLIB能够代理接口和类,JDK只能代理接口

2.据说CGLIB的性能更高

StringAOP 使用的是 JDK 还是 CGlib 什么时候使用 JDK 什么时候使用 CGlib

proxyTargetClass 是 Spring AOP 的一个配置项

proxyTargetClass 值 目标类情况 使用代理方式
false 实现了接口 JDK 代理
false 没有实现接口(只有类) CGLIB 代理
true 实现了接口 CGLIB 代理
true 没有实现接口 CGLIB 代理
  1. proxyTargetClass=false(默认旧版本规则):优先 JDK 代理(类实现接口才用 JDK,没接口自动用 CGLIB);
  2. proxyTargetClass=true:强制全部使用 CGLIB 代理,不管这个类有没有实现接口。

三、版本差异重点(SpringBoot2.x 和 3.x 不一样)

  1. Spring Framework 原生框架:proxyTargetClass=false 默认值;
  2. SpringBoot 2.x 版本:默认也是 false,优先 JDK 代理;
  3. SpringBoot 3.x 默认值改成 true SpringBoot3 全局默认强制使用 CGLIB 代理,不管你的 Bean 有没有实现接口,统一 CGLIB。

这就是为什么你用 SpringBoot3 做 AOP 的时候,不需要写接口也能正常切面拦截,SpringBoot2 不写接口就会报错。

相关推荐
天天进步20152 小时前
UI-TARS 源码解析 #4:UI-TARS 与传统 RPA 的区别:为什么它不是简单的坐标点击脚本?
开发语言
AI人工智能+电脑小能手2 小时前
【大白话说Java面试题 第172题】【07_Redis篇】第8题:`setnx` 做分布式锁存在的问题
java·redis·分布式锁·死锁·setnx
运维行者_3 小时前
广域网性能监控:分布式IT架构下的链路质量保障
开发语言·网络·分布式·后端·架构·数据库架构
梅雅达编程笔记3 小时前
零基础学 Python 第7章 | 字典 dict:键值对存储
开发语言·python·beautifulsoup·numpy·pandas
灯澜忆梦3 小时前
GO---map函数
开发语言·前端·后端·golang
喜欢打篮球的普通人3 小时前
LLVM后端指令选择
android·java·javascript
某不知名網友3 小时前
C++ 七大排序算法完整讲解
java·算法·排序算法
CodeStats3 小时前
《源纹天书》第一百六十一章至第一百六十五章:开源化宣言、元定义的力量、栈帧重构、容器道场升级、技术债的化身!
java·源纹天书
不良手残3 小时前
MyBatisPlus代码自动生成器
java
SamDeepThinking3 小时前
一次线程池线上故障复盘:四层防线如何避免数据丢失
java·后端·程序员