赞
踩
目录
AOP 就是面向切面的编程, 是一种思想,是对某一类事情的集中处理。
例如登陆权限的检验(检测当前用户是否登录),在学习 AOP 前,我们会将这样一个检测机制封装成一个方法,在需要检测的地方调用该方法即可,但想象以下,首先,如果这个方法有 1000 个地方要进行调用, 那么你去一个一个写调用函数很累,其次,一旦这个登陆检查方法需要修改,比如增加一个参数,那么,你就需要修改 1000 方法调用的参数... 但如果你会 AOP 后,我们只需要在某处配置一下,就可以实现用户的登录检测,不需要每一个方法中都写登录检验的调用方法啦!
AOP 是一种思想,而 Spring AOP 是一个框架,提供了对 AOP 思想的实现(类似于 IoC 和 DI 的关系)。
例如刚刚我们所讲到的登录检测机制,这一个方法一旦需要调用的地方多了,不仅写起来麻烦,维护起来成本也是很高的,所以,对于这种功能统一,且使用地方较多的功能,就可以考虑 AOP 来进行统一的处理!
例如以下常见的使用场景:
Ps: AOP 是对某一功能进行的统一处理,大大降低了代码维护的成本,所以可以说 AOP 是 OOP 的补充和完善~
切面,在程序中就是对某一功能进行统一处理的类, 这个类里包含了很多方法,这些方法就是由 切点 和 通知 组成。
用来进行主动拦截的规则(配置)。
这里拦截的是什么,过程是什么样的呢?就是对用户向服务器发送的请求进行拦截,检测用户的操作是否符合预期,发现问题并统一处理的过程,如下图:
通知就是 AOP 的具体执行动作。具体的,在程序中被拦截后会触发一个具体的动作,就是通知中具体实现的业务代码。
在 Spring 中,可以在方法上使用以注解,设置方法为通知方法,被拦截后满足条件就会调用通知方法:
会触发 AOP 规则的所有的点(所以请求)。
- <dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-aop</artifactId>
- </dependency>
Ps:
1.创建 Spring Boot 项目时是没有 Spring AOP 框架可以选择的。
2.添加 Spring AOP 框架可以去中央仓库,值得注意的是要选择 Spring Boot 对应的 AOP ,而不是 Spring 对应的 AOP。
3.最好选择 Spring Boot 对应版本的 AOP ,以上就是 2.7.9版本。
使用 @Aspect 注解修饰类,告诉框架是一个切面类。
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.springframework.stereotype.Component;
-
-
- @Aspect //告诉框架我是一个切面类
- @Component
- public class UserAspect {
-
- }
使用 @Pointcut 修饰一个方法,它不需要由方法体。方法名就是起到一个标识的作用,标识通知方法具体指的是哪一个切点(切点可能有多个)。
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.*;
- import org.springframework.stereotype.Component;
-
- @Aspect //告诉框架我是一个切面类
- @Component
- public class UserAspect {
-
- /**
- * 切点:配置拦截规则
- */
- @Pointcut("execution(* com.example.demo.controller.UserController.*(..))")
- public void pointcut() {
- }
-
- }
AspectJ ⽀持三种通配符,如下:
切点表达式由切点函数组成,其中 execution() 是最常⽤的切点函数,⽤来匹配⽅法,语法如下(注意使用空格进行分割):
execution(<修饰符> <返回类型> <包.类.⽅法(参数)> <异常>)
其中,修饰符和异常可以省略,具体含义如下:
使用通知方法中的五个注解,其中前置通知、后置通知、环绕通知最常用,那么以下代码我将用这三个注解来举例:
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.*;
- import org.springframework.stereotype.Component;
-
- @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 aftereAdvice() {
- System.out.println("执行了后置通知");
- }
-
- /**
- * 环绕通知
- * @param joinPoint
- * @return
- * @throws Throwable
- */
- @Around("pointcut()")
- public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
- System.out.println("进入了环绕通知~");
- Object obj = null;
- obj = joinPoint.proceed();
- System.out.println("退出了环绕通知~");
- return obj;
- }
-
- }
执行结果如下:
Spring 的切面是代理类实现的,包裹了目标对象,也就是说,用户只能先通过代理类,进行校验,如果没有问题才会进一步访问到目标对象。
织入简单理解就是代理生成的时机,一般情况下,在 Spring AOP 动态代理的植入时机是程序的运行期。
Spring AOP 是建立在动态代理的基础上,因此 Spring 对 AOP 的支持局限于方法级别的拦截。
Spring AOP 支持 JDK Proxy 和 CGLIB 方式实现动态代理。默认情况下,实现了接口的类,使用 AOP 会基于 JDK 生成代理类,没有实现接口的类,会基于 CGLIB 生成代理类。
JDK 和 CGLIB 底层都是基于反射实现的。、
1. JDK 实现,要求被代理类必须实现接⼝,之后是通过 InvocationHandler 及 Proxy,在运⾏ 时动态的在内存中⽣成了代理类对象,该代理对象是通过实现同样的接⼝实现(类似静态代理接⼝实现的⽅式),只是该代理类是在运⾏期时,动态的织⼊统⼀的业务逻辑字节码来完成。
2. CGLIB 实现,被代理类可以不实现接⼝,是通过继承被代理类,在运⾏时动态的⽣成代理类对象。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。