作者:~小明学编程
文章专栏:Spring框架
格言:热爱编程的,终将被编程所厚爱。
目录
什么是SpringAOP?
AOP的组成
切面(Aspect)
连接点(Join Point)
切点(Pointcut)
通知(Advice)
SpringAOP的实现
添加依赖
定义切面
设置切点
AspectJ
通知
SpringAOP的原理
织入(代理的生成时机)
动态代理
JDK 及 CGLIB 的⽅式的异同点
AOP 是(Aspect Oriented Programming),也就是面向切面编程。是⼀种思想,是对某⼀类事情的集中处理。Spring AOP 提供了一种对 AOP 思想的实现,是对 OOP(面向对象思想) 思想的扩充。
就拿我之前的项目来说博客系统,里面的任何一个链接你在点击之后都需要先对你的用户名和密码来进行一个验证然后再执行后面的内容,但是问题就来了我们总不能每次添加新的方法或者功能然后都去添加用户验证吧我们想着是把验证这一个功能统一起来,降低与其它代码的耦合性,也就是我们各管各的就行了。
不仅如此:以下这些功能都需要我们去将其统一的处理,不然的话我们每个功能都需要去加这些代码是在是很繁琐。
定义:切⾯(Aspect)由切点(Pointcut)和通知(Advice)组成,它既包含了横切逻辑的定义,也包括了连接点的定义。
上面是切面的定义,看起来晦涩难懂,通俗的来说的话切面定义了在程序中需要被拦截的一组相关的操作。切面可以是一个类、一个接口或者一个注解。
应⽤执⾏过程中能够插⼊切⾯的⼀个点,这个点可以是⽅法调⽤时,抛出异常时,甚⾄修改字段
时。切⾯代码可以利⽤这些点插⼊到应⽤的正常流程之中,并添加新的⾏为。
前面我们说了切面但是我们怎么去连接这个切面呢,这就是我们为什么需要连接点,连接点是去连接我们的切面的。
Pointcut 的作⽤就是提供⼀组规则(使⽤ AspectJ pointcut expression language 来描述)来
匹配 Join Point,给满⾜规则的 Join Point 添加 Advice。
通俗的来说切点就是我们具体的拦截规则,拦截之后然后发送通知。
切面要完成的工作就是通知。就是规定 AOP 执行的时机和执行的方法。
常见的通知注解如下:
AOP只是一种思想而SpringAOP是用代码将我们的这种思想给实现了。
org.springframework.boot
spring-boot-starter-aop
2.7.11
这是我们SpringAOP的依赖,其中版本需要根据自己的springboot版本来进行选择。
@Aspect
@Component
public class UserAspect {
}
其中@Aspect注解是将我们的当前类定义成一个切面。
//作为切点配置拦截规则
@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
public void pointcut() {
}
这里的切点就是配置我们的拦截规则,我们想要拦截什么样子的请求。
上面我们看到Pointcut里面有一串复杂的符号,这些符号是AspectJ的语法下面我们就来说说这些语法。
固定拦截写法:
切点函数:execution(<修饰符><返回类型><包.类.⽅法(参数)><异常>)
接着我们就是在这里面写上我们具体的内容。
AspectJ ⽀持三种通配符
修饰符(一般省略):,如public 公共,* 任意。
返回类型(一般不省略):,void 返回没有值,String 返回值字符串,* 任意。
包:(一般情况下要有但是可以省略)
类(一般情况下要有但是可以省略):UserServicelmpl 指定类,*Impl 以Impl结尾,User 以User开头。
方法名(不可省略):addUser 固定方法,add* 以add开头,*Do 以Do结尾。
参数:()无参,(int)一个参数,(int,int)两个参数,.. 任意参数。
异常:throws 一般省略。
常见的通知注解有下面几种下面逐一的给大家做演示:
@Aspect//此时是一个切面类
@Component//随着框架的启动而启动
public class UserAspect {
//作为切点配置拦截规则
@Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
public void pointcut() {
}
/*
* 前置通知
* */
@Before("pointcut()")
public void beforeAdvice() {
System.out.println("执行了前置规则~");
}
/*
* 后置通知
* */
@After("pointcut()")
public void AfterAdvice() {
System.out.println("执行了后置通知~");
}
//环绕通知
@Around("pointcut()")
public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("执行了环绕通知~");
Object obj = null;
// 执行目标方法
obj = joinPoint.proceed();
System.out.println("退出环绕通知~");
return obj;
}
}
拦截内容:
@RestController
@RequestMapping("/user")
public class UserController {
@RequestMapping("/hi")
public String sayHi(String name) {
System.out.println("执行了sayhi方法");
return "Hi,"+name;
}
@RequestMapping("/hello")
public String sayHello() {
return "hello world";
}
}
接下来我们就看一下我们访问这些接口的时候是否会给我们通知。
可以看到其中的执行顺序,先执行环绕通知,然后前置通知,执行方法替,再执行后置通知,最后是退出我们的环绕通知。
Spring AOP 是构建在动态代理基础上,因此 Spring 对 AOP 的支持局限于方法级别的拦截,Spring AOP 支持 JDK Proxy 和 CGLIB 方式实现动态代理。
我们在前后端交互的时候没有代理的时候前后端是直接进行交互的,但是这样我们就需要去校验前端的一些数据等,如果我们有代理的话那么前端会先将数据传到代理代理做一个处理然后代理再将数据给后端,如此一来我们就可以专注于代码逻辑了。
织⼊是把切⾯应⽤到⽬标对象并创建新的代理对象的过程,切⾯在指定的连接点被织⼊到⽬标对
象中。
在⽬标对象的⽣命周期⾥有多个点可以进⾏织⼊:
动态代理指的是在程序运行时动态地创建一个实现特定接口或一组接口的对象,该对象可以拦截并处理所有传递给它的方法调用。动态代理通常是通过在运行时生成字节码来实现的,从而避免了在编译时手工编写代理类的繁琐过程。
我们学习 Spring 框架中的AOP,主要基于两种⽅式:JDK 及 CGLIB 的⽅式。这两种⽅式的代
理⽬标都是被代理类中的⽅法,在运⾏期,动态的织⼊字节码⽣成代理类。
相同点:
都可以在运行时动态生成代理对象,在代理对象中调用委托类的方法。
区别: