目录
一、什么是AOP?Spring AOP?
和IOC DI一样,AOP只是一种思想,而Spring AOP是一个框架,是AOP思想的实现。
AOP(Aspect Oriented Programming):面向切面编程,它是一种思想,它是对某一类事情的集中处理。比如用户登录权限的效验,没学 AOP 之前,我们所有需要判断用户登录的页面(中的方法),都要各自实现或调用用户验证的方法,然而有了 AOP 之后,我们只需要在某处配置一下,所有需要判断用户登录页面(中的方法)就全部可以实现用户登录验证了,不再需要每个方法中都写相同的用户登录验证了。这样不仅简化了代码,而且实现更加方便。
AOP的功能:
除了统一的用户登录判断之外,AOP 还可以实现:
- 统一日志记录
- 统一方法执行时间统计
- 统一的返回格式设置
- 统一的异常处理
- 事务的开启和提交等
适合使用AOP的情况:
对于功能统一,且使用的地方较多的功能,就可以考虑 AOP来统一处理了。
也就是说使用 AOP 可以扩充多个对象的某个能力,所以 AOP 可以说是 OOP(Object Oriented
Programming,面向对象编程)的补充和完善。
二、AOP的组成
切面(Aspect)
- 通俗点讲就是 处理某方面具体问题的一个类,类里面包含了很多的方法,这些方法就是切点和通知。
切点(Pointcut)
用来进行主动拦截的规则(配置)
通知(Advice)
- 前置通知@Before:通知方法会在目标方法调用前执行
- 后置通知@After:通知方法会在目标方法返回或者抛出异常后调用
- 返回之后通知@AfterReturning:通知方法会在目标方法返回后调用
- 抛异常后通知@AfterThrowing:通知方法会在目标方法抛出异常后通知
- 环绕通知@Around:通知包裹了被通知的方法,在被通知的方法,通知之前和调用之后执行的自定义行为
连接点(Join Point)
- 可能会触发AOP的规则的所有点(请求)
三、Spring AOP的使用
我们大概知道了Spring AOP是干嘛的了,那这个怎么使用呢
使用步骤
①添加的依赖
- 在Spring Boot添加的时候没有直接的AOP,要我们在pom.xml中手动添加。
- 自己找依赖的方法:中央仓库,搜索spring aop,寻找 Spring Boot Starer AOP进去,选择 与 SpringBoot 版本一致的。
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-aop -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
②定义切面(创建切面类)③定义切点(配置拦截规则)④定义通知的实现
package com.example.demo.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component //随着框架的启动而启动
@Aspect //告诉框架我是一个切面类
public class UserAspect {
//切点(配置拦截规则)
@Pointcut("execution(* com.example.demo.controller.UserController.*())")
public void pointcut() {
}
//定义通知
@Before("pointcut()")
public void beforeAdvice() {
System.out.println("beforeAdvice:执行了前置通知");
}
@After("pointcut()")
public void afterAdvice() {
System.out.println("afterAdvice:执行了后置通知");
}
@Around("pointcut()")
public Object doAround(ProceedingJoinPoint joinPoint) {
Object obj = null;
System.out.println("--Around方法开始执行");
try {
// 执行拦截方法
obj = joinPoint.proceed();
} catch (Throwable e) {
e.printStackTrace();
}
System.out.println("--Around方法执行结束");
return obj;
}
}
//打印结果
--Around方法开始执行
beforeAdvice:执行了前置通知
执行了UserController里的内容
afterAdvice:执行了后置通知
--Around方法执行结束
切点拦截表达式的创建
- 修饰符(一般可省略)
- public 公共方法
- * 任意
- 返回值(不能省略)
- void 没有返回值
- String 返回字符串类型
- 包
- com.gyf.crm 固定包
- com.gyf.crm.. crm包下的所有包(包含自身)
- com.gyf.crm.*.service crm下子包任意(例如:com.gyf.crm.user.service)
- com.gyf.crm.*.service.. crm下子包任意,固定目录seivice,service目录任意包
- 类
- UserService 指定类
- *Service 以Service结尾的类
- User* 以User开头的类
- * 任意
- 方法名(不能省略)
- addUser 固定方法
- add* 以add开头的方法
- *User 以User结尾的方法
- * 任意方法
- 参数
- () 无参
- (int) 有一个int的参数
- (int,String)有一个int和一个String的参数
- (..) 参数任意
- throws(可以省略一般不写)
案例:
- execution(* com.cad.demo.User.*(..)) 匹配 User 类里的所有方法
- execution(* com.cad.demo.User+.*(..)) 匹配该类的 类包括该类的所有方法
- execution(* com.cad.*.*(..)) 匹配 com.cad 包下的所有类的所有方法
- execution(* com.cad..*.*(..)) 匹配 com.cad 包下、 孙包下所有类的所有方法
- execution(* addUser(String, int)) 匹配 addUser 法,且参数为String和int
四、Spring AOP实现原理
了解什么是 织入 ?
织入 是把 切面 应用到 目标对象,并创建新的代理对象的过程。切面在指定的连接点被织入到目标对象中。在目标对象的生命周期可以有多个点进行织入:
- 编译器:切面在目标类编译时被织入
- 类加载期:切面在目标类加载到JVM时被织入
- 运行期:切面在应用运行的某一时刻被织入。一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象。SpringAOP就是以这种方式织入切面的。
动态代理
我们学习的AOP主要基于两种方式(JDK和CGLIB),在运行期间织入字节码,生成代理类。这两种方法都采用了反射
- JDK:是Spring框架的默认方式
- CGLIB:是Spring Boot框架的默认方式