Spring中的AOP(面向切面编程)
1. spring 中的面向切面编程
AOP (aspect切面 oriented 面向 programming 编程)
切面 aspect = 通知 adivce + 切点 pointcut
通知:是一个方法,其中包含了重复的逻辑(计时,事务)
切点:是一种匹配条件, 与条件相符合的目标方法,才会应用通知方法
代理:proxy
目标:target
2. 使用步骤
- 添加 maven 依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.22.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.8.13</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>
- 编写切面类
@Aspect
public class 切面类 {
@Around("切点表达式") // 切点表达式用来匹配目标和通知
public Object 通知方法(ProceedingJoinPoint pjp) {
// 写一些重复的逻辑, 计时, 事务控制,权限控制
// 调用目标 , 其中 result 是目标方法返回的结果
Object result = pjp.proceed();
return result;
}
}
-
把切面类和目标类都交给 spring 容器管理
可以用<context:component-scan>
配合 @Component @Service 等注解扫描
也可以使用<bean>
-
使用切面
从容器 getBean 根据接口获取,容器返回代理对象,代理对象中进行切点匹配,匹配到了执行通知,通知内部间接调用目标
// 1. 偷梁换柱, spring 容器返回的是一个代理对象
UserService userService = context.getBean(UserService.class);
System.out.println(userService.getClass());
// 2. 调用的是代理对象的 a 方法
userService.a();
3. 切点表达式
3.1 within 匹配类中的所有方法(粒度粗)
语法:
within(包名.类名)
UserServiceTarget
insert
update
OrderServiceTarget
a
b
c
within(service.*ServiceTarget) // 其中 * 表示所有
3.2 execution 表达式 可以精确到类中的每个方法 (粒度细)
语法:
execution(访问修饰符 返回值 包名.类名.方法名(参数信息))
返回值的位置写 * 表示返回值得类型是任意的
类名 写 * 代表任意的类
方法名中出现 *, 例如下面的表达式会匹配所有以 find 开头的方法
@Around("execution(public * service.UserService.*a())")
但是 * 写在参数位置,只能匹配一个任意类型的参数
@Around("execution(public * service.UserService.a(*))")
a(int) 匹配
a(long) 匹配
a(int, int) 不匹配
a() 不匹配
… 写在参数位置, 匹配任意个数和任意类型的参数
@Around("execution(public * service.UserService.a(..))")
a(int) 匹配
a(long) 匹配
a(int, int) 匹配
a() 匹配
3.3 @annotation
语句:
@annotation(包名.注解名)
看执行方法上有没有这个注解,有的话就算匹配
4. 通知类型
@Around - 环绕通知 (功能最强大, 可以自由定义通知代码的位置)
@Before - 前置通知
@After - 后置通知
@AfterReturning - 正常返回通知
@AfterThrowing - 异常返回通知
这几种通知实际上对应的就是通知代码相对于目标代码的位置
@Before - 前置通知
try {
目标方法
@AfterReturning - 正常返回通知
} catch(Exception e) {
@AfterThrowing - 异常返回通知
} finally {
@After - 后置通知
}
5. 面向切面的应用
- spring 中的事务管理
@Transactional
public void deleteByIds(List<Integer> ids) {
}
// 代理对象.deleteByIds, 代理对象中与@Transactional进行匹配,发现方法上有这个注解,就调用事务通知, 继续调用目标方法
// TransactionInterceptor(跟事务相关的通知代码)
- 统一的权限控制
- 缓存的控制
- 统一的日志记录
6. spring 中如果目标类没有实现接口
- 如果目标对象实现了接口:底层 spring 会调用 jdk的动态代理技术 来生成代理, 要求代理对象和目标对象实现共同的接口
- 如果目标对象没有实现接口:底层 spring 会调用 cglib 代理技术 来生成代理,生成了一个子类作为代理对象