目录
〇、前言
一、AOP中的一些基本概念
二、两个切面的概念
三、advisor的使用
3.1 前置知识
3.2 使用步骤
四、spring对jdk和cglib的统一
〇、前言
对jdk和cglib 实现动态代理的原理不清楚的兄弟们,可以参考前文:Spring原理学习(五):一篇讲清楚动态代理(jdk和cglib)的使用、原理和源码_玉面大蛟龙的博客-CSDN博客
spring当中不需要我们直接去用jdk或者cglib,它提供了ProxyFactory来方便地创建代理,那么他如何选择代理方法呢?我打算通过AOP中的切面来讲解这一部分。
一、AOP中的一些基本概念
复习一下AOP基本的概念,对AOP不熟悉的可以参考我的博客:Spring5学习(七):注解方式进行AOP操作 及 多种通知类型的测试_玉面大蛟龙的博客
切点相当于匹配规则。并不是所有方法都要增强,符合切点的方法才会增强。
通知就是增强的逻辑。
切面 = 切点 + 通知。
二、两个切面的概念
- aspect:可以包含多个切面
aspect =
通知1(advice) + 切点1(pointcut)
通知2(advice) + 切点2(pointcut)
通知3(advice) + 切点3(pointcut)
...
- advisor:更细粒度的切面。包含一个通知和一个切点
aspect在底层逻辑中也是拆分为多个advisor进行处理的,所以我们就使用更细粒度的advisor吧。
三、advisor的使用
3.1 前置知识
这是Adivsor的简要的类图。Adivsor是由切点(Pointcut)和Advice(通知)组成,Pointcut 和 Advice都有极其丰富的实现,我们选熟悉的来使用。
下图是PointCut的实现,我们选择 AspectJExpressionPointcut (根据AspectJ表达式的切点)来实现。
下图是Advice的实现,我们选取 MethodInterceptor 来实现。本质上它是一种环绕通知。
注意,这里的 MethodInterceptor 是 spring使用的 MethodInterceptor,包名为org.aopalliance.intercept.MethodInterceptor,跟我们前面介绍的cglib中的不是一回事,只是同名而已。
3.2 使用步骤
使用切面的步骤如下:
- 备好切点
- 备好通知
- 备好切面
- 创建代理
public class A15 {
public static void main(String[] args) {
/*
两个切面概念
aspect =
通知1(advice) + 切点1(pointcut)
通知2(advice) + 切点2(pointcut)
通知3(advice) + 切点3(pointcut)
...
advisor = 更细粒度的切面,包含一个通知和切点
*/
// 1. 备好切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
// 匹配所有类中的foo方法,因此Target中的bar方法不会被匹配到
pointcut.setExpression("execution(* foo())");
// 2. 备好通知
MethodInterceptor advice = invocation -> {
// 前置增强
System.out.println("before...");
// 调用目标
Object result = invocation.proceed();
// 后置增强
System.out.println("after...");
return result;
};
// 3. 备好切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
// 4. 创建代理
Target1 target = new Target1();
// 这里不需要我们直接去用jdk或者cglib,spring提供了ProxyFactory来方便地创建代理
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisor(advisor);
I1 proxy = (I1) factory.getProxy();
System.out.println(proxy.getClass());
proxy.foo();
proxy.bar();
}
interface I1 {
void foo();
void bar();
}
/**
* Target1实现了接口
*/
static class Target1 implements I1 {
public void foo() {
System.out.println("target1 foo");
}
public void bar() {
System.out.println("target1 bar");
}
}
/**
* Target2没有实现接口
*/
static class Target2 {
public void foo() {
System.out.println("target2 foo");
}
public void bar() {
System.out.println("target2 bar");
}
}
}
运行后发现,使用的代理方式是cglib。那么,spring是如何选择代理方式的呢?
四、spring对jdk和cglib的统一
先说结论:
- proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
- proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现
- proxyTargetClass = true, 总是使用 cglib 实现
介绍一下 ProxyTargetClass:它是ProxyFactory的父类的一个属性:
proxyTargetClass属性其实就是是用来配置是否代理目标类。简而言之就是是否所有的代理对象都通过 CGLIB 的方式来创建,在之后的流程中,Spring 会根据 Bean 实例来判断是采用 JDK 动态代理的方式创建代理对象,还是通过 CGLIB 的方式创建代理对象,如果proxyTargetClass属性配置为true
,则全部采用 CGLIB 的方式。
演示结果:
1、 proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
// 4. 创建代理
Target1 target = new Target1();
// 这里不需要我们直接去用jdk或者cglib,spring提供了ProxyFactory来方便地创建代理
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisor(advisor);
// 告诉工厂,这个方法上继承了什么接口
factory.setInterfaces(target.getClass().getInterfaces());
// 设置ProxyTargetClass参数
factory.setProxyTargetClass(false);
I1 proxy = (I1) factory.getProxy();
System.out.println(proxy.getClass());
proxy.foo();
proxy.bar();
2、 proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现
// 4. 创建代理
Target2 target = new Target2();
// 这里不需要我们直接去用jdk或者cglib,spring提供了ProxyFactory来方便地创建代理
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisor(advisor);
// 告诉工厂,这个方法上继承了什么接口
factory.setInterfaces(target.getClass().getInterfaces());
// 设置ProxyTargetClass参数
factory.setProxyTargetClass(false);
Target2 proxy = (Target2) factory.getProxy();
System.out.println(proxy.getClass());
proxy.foo();
proxy.bar();
3、 proxyTargetClass = true, 总是使用 cglib 实现
目标类继承了接口
// 4. 创建代理
Target1 target = new Target1();
// 这里不需要我们直接去用jdk或者cglib,spring提供了ProxyFactory来方便地创建代理
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisor(advisor);
// 告诉工厂,这个方法上继承了什么接口
factory.setInterfaces(target.getClass().getInterfaces());
// 设置ProxyTargetClass参数
factory.setProxyTargetClass(true);
I1 proxy = (I1) factory.getProxy();
System.out.println(proxy.getClass());
proxy.foo();
proxy.bar();
目标类没继承接口:
// 4. 创建代理
Target2 target = new Target2();
// 这里不需要我们直接去用jdk或者cglib,spring提供了ProxyFactory来方便地创建代理
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisor(advisor);
// 告诉工厂,这个方法上继承了什么接口
factory.setInterfaces(target.getClass().getInterfaces());
// 设置ProxyTargetClass参数
factory.setProxyTargetClass(true);
Target2 proxy = (Target2) factory.getProxy();
System.out.println(proxy.getClass());
proxy.foo();
proxy.bar();