Spring之ProxyFactory的实现
Spring中ProxyFactory动态代理
0、概述
其实Spring中的AOP大大简化了我们的开发。画了个流程图总结一下,如下所示
1、ProxyFactory
这个类是由Spring框架提供的,提供的目的是为了屏蔽掉是使用JDK的接口动态代理还是使用CGLIB的类代理实现方式的具体细节,使用ProxyFactory来做一个统一处理。
使用ProxyFactory可以做到更加精细的控制!比如说具体到类中的哪个方法、对同一类有相同方法或者是功能的做统一处理。
分别来看下对应的案例。
使用JDK
使用JDK的话必须要有接口
public interface UserServiceInter {
void test();
}
public class UserService implements UserServiceInter {
public void test() {
System.out.println("test...");
}
public void hello() {
System.out.println("test...");
}
}
对应的测试案例:
public class ProxyFactoryTest1 {
public static void main(String[] args) {
UserServiceInter target = new UserService();
ProxyFactory proxyFactory = new ProxyFactory();
// 工厂代理的对象
proxyFactory.setTarget(target);
// 设置接口
proxyFactory.addInterface(UserServiceInter.class);
// 已知要被代理的对象的前提,然后利用方法拦截器来进行执行
proxyFactory.addAdvice(new MethodInterceptor() {
@Nullable
@Override
public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
});
// 生成代理对象!可以做到有条件的来生成得到对应的对象
UserServiceInter targetProxy = (UserServiceInter) proxyFactory.getProxy();
targetProxy.test();
}
}
使用CGLIB
还是上面的类,但是这里的测试代码中需要修改一下,去掉添加的接口,如下所示:
public class ProxyFactoryTest1 {
public static void main(String[] args) {
UserService target = new UserService();
ProxyFactory proxyFactory = new ProxyFactory();
// 工厂代理的对象
proxyFactory.setTarget(target);
// 已知要被代理的对象的前提,然后利用方法拦截器来进行执行
proxyFactory.addAdvice(new MethodInterceptor() {
@Nullable
@Override
public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
});
// 生成代理对象!可以做到有条件的来生成得到对应的对象
UserService targetProxy = (UserService) proxyFactory.getProxy();
targetProxy.test();
}
}
2、动态代理对于JDK还是CGLIB的选择
那么问题来了,Spring利用ProxyFactory工具类是怎么知道到底是使用JDK动态代理还是CGLIB动态代理呢?
只需要关注如下代码:
ProxyFactory proxyFactory = new ProxyFactory();
...
proxyFactory.getProxy();
进入到源码中查看:
public Object getProxy() {
// 然后调用JDK或者是CGLIB对应的得到代理对象的方法
return createAopProxy().getProxy();
}
点击进入到createAopProxy方法中来:
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
// 默认为DefaultAopProxyFactory
// 先选定使用哪种代理方式:JDK还是CGLIB
return getAopProxyFactory().createAopProxy(this);
}
注意这里的this就是上面的proxyFactory对象,然后进入到createAopProxy方法中来
是不是指定的GraalVM虚拟机这个不由我们来进行控制,我们只需要关注下面的三个条件判断。
1、判断使用JDK还是CGLIB的条件
1.1、是否设置优化
为了需要设置优化呢?因为早期Spring认为CGLIB动态代理的效率比JDK动态代理效率高。但是随着JDK版本升级,二者性能上相差不大。
可以使用ProxyFactory来进行设置
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setOptimize(true);
optimize属性默认值是false,可以设置为true。
1.2、是否指定了代理目标类
是对类代理还是对接口来进行代理。proxyTargetClass属性默认为false
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setProxyTargetClass(true);
1.3、是否指定代理接口
是否在ProxyFactory对象中设置了接口,如果没有设置了接口,可以使用JDK动态代理
DService dService = new DService();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(dService);
proxyFactory.addInterface(MyInterface.class);
proxyFactory.addAdvisor(new MyAdvisor3());
MyInterface proxy = (MyInterface) proxyFactory.getProxy();
proxy.caculate();
需要注意的是,上面的各种设置可以组合搭配起来进行使用。注意调用顺序
因为三个条件是或者判断,如果前面的满足了条件,后面的判断就不会进入了。
知道了使用哪个动态代理之后,那么就应该来创建对应的代理对象了。
2、具体代理选择
2.1、JDK动态代理
那么先来看下ProxyFactory使用JDK动态代理之后的效果,在org.Springframework.aop.framework.JdkDynamicAopProxy#getProxy()
public Object getProxy() {
return getProxy(ClassUtils.getDefaultClassLoader());
}
看下具体的实现
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
// this实现了InvocationHandler,表示调用当前类中的invoke方法来进行实现
return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}
看下具体的实现过程
具体说来,分为以下几个步骤:
- 1、获取得到被代理对象的来源;
- 2、如果代理对象调用的是Equals、HashCode等方法的时候,直接调用不走代理过程;
- 3、是否需要将代理对象暴露到当前线程中来。在代理对象执行方法的时候,可以在方法内部获取得到代理对象;
- 4、执行被代理对象来源中的getTarget方法,获取得到被代理对象,即target对象;
- 5、找到ProxyFactory对象中设置的拦截器并执行拦截器中的拦截方法;
- 6、做一些清理工作;如:保存到线程中的代理对象等;
因为可以在ProxyFactory对象中设置了多个通知和匹配表达式,切入点表达式表示是横向针对哪些切面来进行拦截,对于通知来说,对于拦截到的面增强的逻辑是什么。
2.2、案例
接口和实现类:
public interface MyInterface {
void caculate();
}
public class DService implements MyInterface {
@Override
public void caculate() {
System.out.println("caculate execute............");
}
}
对应的Advisor如下:
public class MyAdvisor3 implements PointcutAdvisor {
@Override
public Advice getAdvice() {
return new MyAdvice();
}
@Override
public boolean isPerInstance() {
return false;
}
@Override
public Pointcut getPointcut() {
// 只匹配方法名称是caculate的方法名称
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return method.getName().equals("caculate");
}
};
}
}
对应的Advice如下所示:
public class MyAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("hello,MyAdvice#before");
}
}
对应的测试代码如下:
DService dService = new DService();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(dService);
proxyFactory.addInterface(MyInterface.class);
proxyFactory.addAdvisor(new MyAdvisor3());
proxyFactory.addAdvice(new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("before");
}
});
MyInterface proxy = (MyInterface) proxyFactory.getProxy();
proxy.caculate();
3、找到拦截器并执行方法
重点就来到了获取得到拦截器以及执行拦截器中的方法
来到org.Springframework.aop.framework.DefaultAdvisorChainFactory#getInterceptorsAndDynamicInterceptionAdvice中来,那么看看这里是如何来进行寻找对应的拦截器的。(从方法名称上来看,这里是分成了两种:Interceptor和DynamicInterceptionAdvice,在之后会分别来进行介绍)
看下面代码:
Advisor[] advisors = config.getAdvisors();
首先会将ProxyFactory对象中设置的Advisor拿出来
3.1、什么是Advisor?
那么什么是Advisor呢?Advisor是由PointCut和Advice组合而成的。
PointCut首先来判断哪些方法可以被代理,而Advice表示的是代理的逻辑是什么?
3.2、什么是PointCut?
PointCut表示的是切入点表达式,从横向上来进行匹配,要对哪些方法来进行切割,看下接口:
public interface Pointcut {
ClassFilter getClassFilter();
MethodMatcher getMethodMatcher();
Pointcut TRUE = TruePointcut.INSTANCE;
}
存在着类过滤器和方法过滤器
- 类过滤器指的是如果指定的类条件进行筛选,满足之后才会来进行下一步;
- 方法匹配器指的是在类过滤器满足了的情况下,对方法的各种条件进行筛选,满足了进行下一步
对应的结构如下所示:
类过滤器
public interface ClassFilter {
boolean matches(Class<?> clazz);
ClassFilter TRUE = TrueClassFilter.INSTANCE;
}
match方法是Class,表示可以从类信息上来进行判断。
方法匹配器
public interface MethodMatcher {
boolean matches(Method method, Class<?> targetClass);
boolean isRuntime();
boolean matches(Method method, Class<?> targetClass, Object... args);
MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}
在方法匹配器中有两个match方法,为方法重载。
- 对于第一个matches来说,传入进去的是目标类和方法对象;
- 对于第二个matches来说,只有在isRuntime=true的情况下,才会执行第二个matches方法,比第一个多一个参数。
3.3、Advice和Advisor的关系
代码如下所示:
UserService target = new UserService();
ProxyFactory proxyFactory = new ProxyFactory();
// 工厂代理的对象
proxyFactory.setTarget(target);
// 已知要被代理的对象的前提,然后利用方法拦截器来进行执行
proxyFactory.addAdvice(new MethodInterceptor() {
@Nullable
@Override
public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
System.out.println("before...");
Object result = invocation.proceed();
System.out.println("after...");
return result;
}
});
据我们所示,我们添加的应该是Advisor,而不是Advice。所以Advisor和Advice之间是什么关系呢?
实际上,在代码proxyFactory.addAdvice方法中,在源码中如下所示:
// 将Advice封装成DefaultPointcutAdvisor对象,里面的PointCut默认是匹配所有的
addAdvisor(pos, new DefaultPointcutAdvisor(advice));
将advice包装成了一个默认的DefaultPointcutAdvisor。那么回到上面所说的,对于Advisor来说,都是由PointCut和Advice组成的,那么现在有了Advice,那么PointCut是什么呢?
在DefaultPointcutAdvisor中可以看到
public DefaultPointcutAdvisor(Advice advice) {
this(Pointcut.TRUE, advice);
}
PointCut是Pointcut.TRUE,那么这个对象肯定也有类过滤器和方法匹配器,那么看一下对应的匹配规则
public ClassFilter getClassFilter() {
return ClassFilter.TRUE;
}
public MethodMatcher getMethodMatcher() {
return MethodMatcher.TRUE;
}
观察下二者中的match方法,发现对应的match方法如下所示:
ClassFilter中的匹配规则:
public boolean matches(Class<?> clazz) {
return true;
}
MethodMatcher中的匹配规则:
public boolean matches(Method method, Class<?> targetClass) {
return true;
}
说明对于默认的DefaultPointcutAdvisor来说,对于类过滤器和方法匹配器来说,是不会进行任何过滤拦截即可得到代理对象的,只会调用对应的增强逻辑来完成。
3.4、Advisor和MethodIntecepter关系
看下下面的代码:
proxyFactory.addAdvisor(new MyAdvisor3());
proxyFactory.addAdvice(new MethodBeforeAdvice() {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("before");
}
});
我们在上面看到一行代码如下所示:
Advisor[] advisors = config.getAdvisors();
那么对于Advisor和MethodIntecepter关系是什么呢?
3.5、具体匹配操作
下面开始对得到的Advisor来进行匹配操作
1、PointcutAdvisor解析过程
这里将Advisor分为了三类:PointcutAdvisor、IntroductionAdvisor以及其他类型。
在上面的代码中
proxyFactory.addAdvisor(new MyAdvisor3());
这里的MyAdvisor3就是一个PointcutAdvisor
public class MyAdvisor3 implements PointcutAdvisor {
@Override
public Advice getAdvice() {
return new MyAdvice();
}
@Override
public boolean isPerInstance() {
return false;
}
@Override
public Pointcut getPointcut() {
return new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
return method.getName().equals("caculate");
}
};
}
}
当前的Advisor中就存在着Advice+Pointcut
- 对于PointCut来说,方法过滤器没有处理,只对方法来进行处理,只要方法名称命名是caculate即可;
- 对于Advice来说,增强的逻辑如下所示:
public class MyAdvice implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("hello,MyAdvice#before");
}
}
那么看一下对PointcutAdvisor的解析过程:
首先调用类过滤器中的matches方法,匹配之后,再调用方法匹配器的matches方法,如果二者都匹配了,会将advisor转换成MethodInterceptor对象。
具体是如何来进行转换的待会儿再看,那么会再次判断方法匹配器中的isRuntime方法,如果返回为true,那么会将MethodInterceptor包装一层,然后将方法中的参数传入进去,创建一个InterceptorAndDynamicMethodMatcher对象放入到集合中去。
2、将Advice转换成MethodInterceptor操作
那么接下来看下,是如何将advice转换成MethodInterceptor的,首先获取得到advisor中的advice对象:
如果advice是MethodInterceptor类型的,那么直接添加到interceptors集合中来。
如果advice是MethodBeforeAdvice、AfterReturningAdvice、ThrowsAdvice的一种,那么将会调用AdvisorAdapter适配器来进行转换。
看一下AdvisorAdapter中的两个方法:
public interface AdvisorAdapter {
// 判断是否支持
boolean supportsAdvice(Advice advice);
// 如果支持就将Advisor转换成MethodInterceptor
MethodInterceptor getInterceptor(Advisor advisor);
}
可以看下具体的转换过程:
MethodBeforeAdviceAdapter
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof MethodBeforeAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice) advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}
如果Advice是MethodBeforeAdvice类型的,那么将调用getInterceptor将Advisor转换成MethodInterceptor,看下具体的转换过程。
public class MethodBeforeAdviceInterceptor implements MethodInterceptor, BeforeAdvice, Serializable {
private final MethodBeforeAdvice advice;
public MethodBeforeAdviceInterceptor(MethodBeforeAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable {
// 首先先执行代理之前的逻辑
this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis());
// 然后执行链路中下一个Advice中的逻辑
return mi.proceed();
}
}
因为在MethodInterceptor是存在着invoke方法的,可以看到在代理方法执行之前。
AfterReturningAdviceAdapter
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof AfterReturningAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
AfterReturningAdvice advice = (AfterReturningAdvice) advisor.getAdvice();
return new AfterReturningAdviceInterceptor(advice);
}
}
public class AfterReturningAdviceInterceptor implements MethodInterceptor, AfterAdvice, Serializable {
private final AfterReturningAdvice advice;
public AfterReturningAdviceInterceptor(AfterReturningAdvice advice) {
Assert.notNull(advice, "Advice must not be null");
this.advice = advice;
}
@Override
@Nullable
public Object invoke(MethodInvocation mi) throws Throwable {
// 首先获取得到结果,然后又会执行到下面的代码
Object retVal = mi.proceed();
this.advice.afterReturning(retVal, mi.getMethod(), mi.getArguments(), mi.getThis());
return retVal;
}
}
首先来执行获取得到结果之后,才会执行最终的方法。
ThrowsAdviceAdapter
class ThrowsAdviceAdapter implements AdvisorAdapter, Serializable {
@Override
public boolean supportsAdvice(Advice advice) {
return (advice instanceof ThrowsAdvice);
}
@Override
public MethodInterceptor getInterceptor(Advisor advisor) {
return new ThrowsAdviceInterceptor(advisor.getAdvice());
}
}
看下org.Springframework.aop.framework.adapter.ThrowsAdviceInterceptor中的方法
要求方法名称必须得是:afterThrowing,而且方法参数个数必要要是1个或者是4个,而且最后一个参数必须要是Throwable异常类型及其子类。
在exceptionHandlerMap中key对应的是异常类型,value为对应的方法对象;然后看下对应的invoke方法
在调用结果中出现了异常,那么会根据异常类型从exceptionHandlerMap中来进行查找,找到了对应的方法
至此,大致上清晰了。
3.5、代理对象执行方法
那么再次回到上面,这个时候已经找到了所有的方法拦截器,但是还没有开始进行调用,下面看看调用的时候需要来做什么事情。
// We need to create a method invocation...
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();
传入进来的参数是代理对象、目标对象、方法、方法参数、目标类和拦截器链
新建对象是:ReflectiveMethodInvocation,那么看下ReflectiveMethodInvocation中的proceed方法
- 1、currentInterceptorIndex的初始值是-1,如果拦截器链路中什么都没有的话,那么会执行连接点方法;
- 2、开始按照ProxyFactory中添加的MethodIntercepter的顺序开始调用的对应的方法拦截器中的方法;这里没有看到任何for循环,但是在ProxyFactory是可以添加多个的;
- 3、按照分类条件来进行执行。①如果是InterceptorAndDynamicMethodMatcher类型的,则根据MethodMatcher中match方法进行匹配,这个时候取决于isRuntime,来决定是调用两个参数的match方法还是三个参数的match方法;②如果是MethodInterceptor类型的,则会调用对应的invoke方法
那么没有看到任何for循环,如何实现多个方法拦截器的多个调用的呢?
可以看到两个地方:invoke(this)和proceed()方法,对于proceed()方法来说,在调用的时候肯定就是递归调用,会再次来到当前方法中调用。
而对于invoke方法来说,MethodBeforeAdviceInterceptor、AfterReturningAdviceInterceptor、ThrowsAdviceInterceptor,又都会调用到proceed()方法这里来。
所以最终执行完成了拦截器中的整个通知方法。
非常清晰明白,至此,利用ProxyFactory产生的代理对象调用方法过程结束。
4、CGLIB动态代理
CGLIB的和JDK的非常类似,在org.Springframework.aop.framework.CglibAopProxy#getProxy(java.lang.ClassLoader)方法中
// 获取和被代理类所匹配的Advisor=====advice+pointcut
Callback[] callbacks = getCallbacks(rootClass);
在getCallbacks方法中,使用下面这行代码得到对应的Callback
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
然后会调用DynamicAdvisedInterceptor类中的intercept方法来进行使用:
和ProxyFactory中的动态代理类似
5、小结
通过上面的JDK的动态代理,有以下总结:
- 1、可以在代码运行过程中,拿到代理对象。前提是在proxyfactory中设置exposeProxy为true;通过AopContext.getCurrentProxy可以获取得到;
- 2、在proxyfactory中可以获取得到多个advise,将advice适配成对应的方法拦截器,然后在调用的时候依次执行里面被代理方法的逻辑;执行完链路的最后一个时,会来执行最终的被代理方法(如果没有设置isRuntime的时候);如果设置了runTime方法,那么就会立即来执行其中的逻辑;
- 3、advice也会被封装成一个advisor,用一个DefaultPointcutAdvisor来进行封装,只不过封装的ClassFilter和MethodMatcher都会为true,也就是说只要是代理对象设置好了,都会来执行这里面的方法;
- 4、自定义PointcutAdvisor的时候,可以指定对类的筛选、对方法的筛选,以及筛选匹配成功之后的被代理方法的逻辑。
- 5、ProxyFactory利用CGLIB来做动态代理以及利用JDK来做动态代理的情况;
画个图总结一下对应的流程: