Spring——创建代理对象-Java Proxy方法分析(JdkDynamicAopProxy)
开始从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<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
if (chain.isEmpty()) {
// 因为没有,所以这里就不创建MethodInvocation,只是直接的调用目标方法。
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse); // 如果没有找到合适的intercept,直接调用的是target(别代理的bean的方法)
}
else {
// 创建MethodInvocation,开始调用
MethodInvocation invocation =
new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
retVal = invocation.proceed();
}
// 拿到方法返回的类型
Class<?> returnType = method.getReturnType();
//方法返回了this对象,并且当前的代理对象是否是returnType的实例对象,并且方法还不是RawTargetAccess的,直接返回代理对象
if (retVal != null && retVal == target &&
returnType != Object.class && returnType.isInstance(proxy) &&
!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
// Special case: it returned "this" and the return type of the method
// is type-compatible. Note that we can't help if the target sets
// a reference to itself in another returned object.
retVal = proxy;
}
else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
throw new AopInvocationException(
"Null return value from advice does not match primitive return type for: " + method);
}
// 返回原来的值
return retVal;
}
finally {
// 释放targetSource里面的target
if (target != null && !targetSource.isStatic()) {
// Must have come from TargetSource.
targetSource.releaseTarget(target);
}
if (setProxyContext) {
// 设置为null
AopContext.setCurrentProxy(oldProxy);
}
}
}
复制代码
下面的分析分为两个部分: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<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
// 拿到适配器的Registry。在spring里面,用来持有对象的一般都叫做Registery
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
// 拿到所有的Advisor,添加进去的Advice会被包装为Advisor。所以,在创建代理对象的时候主要是Advisor在起作用
Advisor[] advisors = config.getAdvisors();
//
List<Object> interceptorList = new ArrayList<>(advisors.length);
Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
Boolean hasIntroductions = null;
for (Advisor advisor : advisors) {
// 对于PointcutAdvisor的话,就需要拿到切入点,来做匹配。
if (advisor instanceof PointcutAdvisor) {
// Add it conditionally.
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
// 拿到切入点做匹配
// 先看class
if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
// 在看method的匹配器,做匹配。
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
boolean match;
// 如果当前的Advisor的方法的匹配器是IntroductionAwareMethodMatcher。
if (mm instanceof IntroductionAwareMethodMatcher) {
if (hasIntroductions == null) {
// 先判断advisors里面自己的IntroductionAdvisor
hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
}
// 在判断外面的
match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
}
else {
// 就走正常的判断,比如AspectJ,注解等等匹配器
match = mm.matches(method, actualClass);
}
if (match) {
// 找到了,就通过适配器来做适配,
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
if (mm.isRuntime()) {
// 如果是runtime还会在InvocationHandle里面判断一次。所以,这里又封装了一下InterceptorAndDynamicMethodMatcher。
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
}
else {
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
}
// 如果是IntroductionAdvisor,这个匹配不像上面的那样复杂,通过getClassFilter来做匹配。但是这个默认的匹配器返回值都是true
else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
else {
// 利用适配器做兼容。
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
复制代码
结束了,构建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
里面去。
到此,结束了。
关于博客这件事,我是把它当做我的笔记,里面有很多的内容反映了我思考的过程,因为思维有限,不免有些内容有出入,如果有问题,欢迎指出。一同探讨。谢谢。