开始从JdkDynamicAopProxy分析
上面的类图是主体的思想。
- 首先要创建代理对象,JDK创建代理对象的时候要传递一个InvocationHandle,JdkDynamicAopProxy自己实现了InvocationHandle。在调用invoke方法的时候,会通过AdvisedSupport调用AdvisorChainFactory获取一个由MethodIntercept组成的列表,
- 调用的时候,将所需要的对象封装为MethodInvocation,做调用。这个调用主要是针对上一步封装的好的MethodIntercept的集合。
带着总纲,开始代码分析
getProxy方法分析获取代理对象,本质是通过Proxy来创建代理对象,通过Proxy来创建代理对象的时候,需要传递要实现的接口和InvocationHandler.
InvocationHandler就是当前的JdkDynamicAopProxy。
所以,下面的代码的主要功能就是确定接口,并且通过Proxy来创建代理对象。
通过AopProxyUtils.completeProxiedInterfaces(this.advised, true)来确定要实现的接口。这个方法里面通过传递进来的AdvisedSupport来确定接口,并且按照情况添加几个特殊的接口,(SpringProxy,Advised,DecoratingProxy)。AdvisedSupport里面接口的来源就是通过ProxyFactory添加进来的。
并且在findDefinedEqualsAndHashCodeMethods方法设置hashcode和equals的标志位。
问题
- 为啥要添加那几个特殊的接口?
- 为啥要单独的找到equals和hashcode的标志位。如果说采取单独处理的话?会怎么处理?为啥要单独处理。
@Override public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource()); } // 确定接口,获取Advised里面的配置信息,并且对于不同的情况添加SpringProxy和Advised和DecoratingProxy Class>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true); // 设置是否有equal和hashcode的方法,对应有两个标志位 findDefinedEqualsAndHashCodeMethods(proxiedInterfaces); // 这就是正常的通过Proxy来创建代理对象。 return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); }invoke方法分析
在JdkDynamicAopProxy里,他自己就是InvocationHandler,所以,重点要看他的invoke方法。
方法的主要作用在一开始的时候已经交代清楚了,主要就是构建MethodIntercept chain。构建MethodInvocation来调用。
此外,在这个里面会对hashcode,equals,还有DecoratingProxy,Advised的接口的特殊的处理。
每个通过Spring创建的代理对象,都会给一个SpringProxy接口,如果opaque为false。就会给一个Advised接口,此外,如果decoratingProxy为true,就会在给一个DecoratingProxy接口。
opaque是ProxyConfig的属性,他表示是否要给代理类添加一个Advised接口,true表示不添加,false表示添加。
decoratingProxy是AopProxyUtils#completeProxiedInterfaces的参数,true表示会添加一个DecoratingProxy接口,否则就不会。通过这个接口会拿到被代理类的类对象。
问题
下面对Advised接口方法的处理,Advised是什么时候添加进来的?调用这个方法的时候具体调用的是什么?
Advised是在构建代理对象的时候添加进来的(具体在AopProxyUtils#completeProxiedInterfaces方法里面)
Advised接口本身的意思就是创建代理对象时候的配置信息。
实际的调用是 调用了一开始传递给JdkDynamicAopProxy的AdvisedSupport。因为在创建代理的时候,ProxyFactory本身就是一个实现了Advised接口的类,在创建的时候,就会将自己传递给JdkDynamicAopProxy。因为创建代理对象是线程安全的,也就是说配置不是共享的,所以,对于一个代理对象来说,实现了这个接口,获取关于自己的配置信息,就直接调用AdvisedSupport的方法就好了。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; // 这是目标bean,TargetSource这个接口的功能是,他持有一个被代理的Bean TargetSource targetSource = this.advised.targetSource; Object target = null; try { // 如果接口方法里面没有equals,但是他调用的确实equals方法, if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { // The target does not implement the equals(Object) method itself. return equals(args[0]); } // 如果接口里面没有hashcode,但是他调用的确实hashcode方法 else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { // The target does not implement the hashCode() method itself. return hashCode(); } // 如果是调用的方法是DecoratingProxy else if (method.getDeclaringClass() == DecoratingProxy.class) { // There is only getDecoratedClass() declared -> dispatch to proxy config. return AopProxyUtils.ultimateTargetClass(this.advised); } // 调用的方法如果是Advised接口里面,因为Advised是在创建代理对象的时候调用的。 // 其实这里调用的是AdvisedSuooper的方法。因为他也实现了这个方法 else if (!this.advised.opaque && method.getDeclaringClass().isInterface() && method.getDeclaringClass().isAssignableFrom(Advised.class)) { // Service invocations on ProxyConfig with the proxy config... return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args); } Object retVal; // 是否要暴露代理, if (this.advised.exposeProxy) { // 将当前的代理对象设置在ThreadLocal里面 oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // 拿到被代理的类 target = targetSource.getTarget(); Class> targetClass = (target != null ? target.getClass() : null); // 重点,拿到拦截器组成的链 List
下面的分析分为两个部分:1. 创建MethodIntercept chain,2. 构建MethodInvocation 调用。
构建MethodIntercept chain从上面的类图可以看出,主要是通过AdvisorChainFactory来创建MethodIntercept chain。他的实现类就一个DefaultAdvisorChainFactory。
代码主要流程如下:
-
遍历Advisor(从Advised中获取到的)
-
在遍历的时候判断不同的情况。
-
PointcutAdvisor(这个处理就比较复杂)
拿到他的切入点里面的ClassFilter做匹配。在后去joinPoint里面的MethodMatch做匹配。如果MethodMatch是IntroductionAwareMethodMatcher,就会从Advisor中判断是否有IntroductionAdvisor。
在两者都满足的情况下,通过适配器做适配,添加到结果里面返回。
-
IntroductionAdvisor
只是通过ClassFilter来做匹配,这个默认值是True。通过适配器做适配,添加到结果里面返回
-
其他
剩下的直接通过适配器做适配。添加到结果返回
-
-
如果适配器里面没有找到,就会报错。
问题?
- 适配器长什么样子?
适配器是要提供了两个方法。
- Support,这个本质是instanceof来判断
- getInterceptor,本质就是获取Advisor里面的Advice,然后自己包装了一下,创建对应的MethodIntercept。
为啥对于MethodMatch为IntroductionAwareMethodMatcher要做处理,并且hasMatchingIntroductions方法里面是做了什么事情?
IntroductionAwareMethodMatcher继承了MethodMatcher,在匹配的时候考虑到了Introductions。具体的判断就要在子类里面去判断了。
hasMatchingIntroductions里面主要配置里面的advisors是否有IntroductionAdvisor类型,并且IntroductionAdvisor里面的classFilter是否是满足的。
Advised的isPreFiltered的作用什么什么?因为在做类适配的时候,有两个或关系的条件。它就是第一个。
设置为true的时候,可以跳过之后的ClassFilter的检查,他们叫做预先过滤。如果跳过的话,其实没有太大的影响,只是跳过了ClassFilter的检查,但是如果Advice不合理,还是会报错。因为适配器里面不支持,直接接报错。
mm.isRuntime()的意思是什么?
如果是true,对已经找到的拦截器,用InterceptorAndDynamicMethodMatcher封装,在利用InvocationHandle做调用的时候,会再次判断一次,这个判断会会将参数传递过去,调用的是boolean matches(Method method, Class> targetClass, Object... args);做判断
@Override public List
结束了,构建MethodIntercept chain结束了,下面来看看几个适配器里面的几个适配器的类
DefaultAdvisorAdapterRegistry#getInterceptors方法来看,通过ProxyFactory创建代理对象的时候,添加的Advice必须是MethodInterceptor的子类。否则就会报错。
此外,在获取MethodIntercept chain的时候还带有缓存,key是方法,v是chain。
构建MethodInvocation调用到这里,MethodIntercept Chain就构建好了,可以构建MethodInvocation来做调用的
还有一个点忘了说了
如果MethodIntercept chain 为空,就会直接调用被代理对象的方法。在不为空的时候,才会构建MethodInvocation
还是从类图开始分析
根接口是JoinPoint。MethodInvocation继承与Invocation,Invocation继承与JoinPoint。ReflectiveMethodInvocation实现了ProxyMethodInvocation接口,并且ReflectiveMethodInvocation依赖ProxyMethodInvocation。同样的cglibMethodInvocation也是。
这里面重要的方法是proceed(),得在此强调接口的功能呢,接口是用来做功能的,具体的实现的细节是在实现类里面。面向接口编程,可以让主体的功能不受影响。在不同的接口里面添加不同的功能。慢慢的套,具体的功能就出来了。
比如下面我们要分析ReflectiveMethodInvocation,他实现了ProxyMethodInvocation,他在MethodInvocation的基础上,增加了对代理对象的持有。
先看ReflectiveMethodInvocation的构造方法和属性
通过构造方法设置属性,Proxy表示代理对象,target表示被代理的对象,method:当前调用的方法,arguments表示当前调用这个方法传递的参数,
targetClass:被代理对象的类对象,interceptorsAndDynamicMethodMatchers就是上一步组装好的MethodIntercept chain。currentInterceptorIndex:表示的是当前chain的下标。
重点还是看Proceed方法
Proceed方法分析public Object proceed() throws Throwable { // 如果是到最后一个了。所有的MethodInterceptor会调用完了之后,才会调用真正的方法,也就是被代理的方法, if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { // 才会调用真正的方法,还是通过 AopUtils.invokeJoinpointUsingReflection()不过,这一次,传递的是target return invokeJoinpoint(); } // 拿到当前的MethodIntercept,currentInterceptorIndex默认值是-1,++为0; Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex); // InterceptorAndDynamicMethodMatcher,这是在上一步构建MethodInceptor chain构建的,如果method match的isRuntime方法返回true,就会构建InterceptorAndDynamicMethodMatcher,这里会再次判一次, if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) { InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice; // Class> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass()); if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) { // 开始调用MethodIntercept。 return dm.interceptor.invoke(this); } else { // 如果上一步是匹配,就跳过这个拦截器。 return proceed(); } } else { // 这里就调用拦截器了 return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this); } }
从类图来看,ReflectiveMethodInvocation依赖MethodInterceptor,MethodInterceptor依赖MethodInvocation。本质就是两者互相依赖,拦截器链里面所有的拦截器都调用结束了,才会调真正的方法。
下面画个图说明一下
比如,现在有两个methodIntercept,一个是MethodBeforeAdviceInterceptor,一个是ThrowsAdviceInterceptor。他俩组成了一个拦截器的链
来看看他两的重点方法
MethodBeforeAdviceInterceptor
@Override public Object invoke(MethodInvocation mi) throws Throwable { this.advice.before(mi.getMethod(), mi.getArguments(), mi.getThis()); return mi.proceed(); }
ThrowsAdviceInterceptor
@Override public Object invoke(MethodInvocation mi) throws Throwable { try { return mi.proceed(); } catch (Throwable ex) { Method handlerMethod = getExceptionHandler(ex); if (handlerMethod != null) { invokeHandlerMethod(mi, ex, handlerMethod); } throw ex; } }
代码表现来看,就是这个样子,那么这个在模式在日后是可以直接仿照的,直接写起来。
问题?
这里有拦截器的顺序吗?
没有,从ProxyFactory添加Advice一来,没看到排序的 *** 作,也就是说,顺序就是添加时候的顺序,但是需要注意,添加的时候他提供了可以指定下标的。
可以在下面的一个例子里面跑demo看看,debug下面看的很清楚。代理对象的创建,MethodIntercept的构成,MethodInvocation调用对Method,看的很清楚。
public class TestProxyFactory { public static void main(String[] args) { try { T t = new T(); ProxyFactory proxyFactory = new ProxyFactory(); proxyFactory.setTarget(t); Class>[] interfaces = t.getClass().getInterfaces(); for (Class> anInterface : interfaces) { proxyFactory.addInterface(anInterface); } ThrowsAdviceInterceptor throwsAdviceInterceptor = new ThrowsAdviceInterceptor(new MethodException()); proxyFactory.addAdvice(throwsAdviceInterceptor); proxyFactory.addAdvice(new MyMethodBeforeTest()); Do proxy = (Do) proxyFactory.getProxy(); String test = proxy.ddo("test"); System.out.println(test); } catch (Throwable e) { e.printStackTrace(); } } public static class MethodException implements ThrowsAdvice { public void afterThrowing(Exception ex) throws Throwable { System.out.println(ex + "exexex"); } } public static class MyMethodBeforeTest implements MethodBeforeAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("------------------------------"); System.out.println(method.getName()); System.out.println(target.getClass()); System.out.println("------------------------------"); } } }
到这里,spring中利用JDK来创建代理对象的方法分析结束了。
补充说明
-
添加到ProxyFactory的Advice会被包装为啥?
有两种,DefaultIntroductionAdvisor和DefaultPointcutAdvisor,如果添加的advice实现了IntroductionInfo接口,就会获取IntroductionInfo#getInterfaces来获取需要引入的接口,添加到DefaultIntroductionAdvisor的interfaces里面。
-
之前说IntroductionInfo接口,可以添加接口。上面没有添加接口的 *** 作,接口是在哪里添加的?
通过IntroductionInfo添加接口,理论上来说,必须在创建代理对象的时候将接口确定好,所以,这个添加 *** 作必须是在这个前面,在添加advice的时候添加的。对于实现了IntroductionInfo的接口,会构建DefaultIntroductionAdvisor,然后会在 AdvisedSupport#validateIntroductionAdvisor里面会添加到interfaces里面去。
到此,结束了。
关于博客这件事,我是把它当做我的笔记,里面有很多的内容反映了我思考的过程,因为思维有限,不免有些内容有出入,如果有问题,欢迎指出。一同探讨。谢谢。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)