Spring——创建代理对象-Java Proxy方法分析(JdkDynamicAopProxy)

Spring——创建代理对象-Java Proxy方法分析(JdkDynamicAopProxy)

开始从JdkDynamicAopProxy分析

image-20211210200604619.png

上面的类图是主体的思想。

  1. 首先要创建代理对象,JDK创建代理对象的时候要传递一个InvocationHandle,JdkDynamicAopProxy自己实现了InvocationHandle。在调用invoke方法的时候,会通过AdvisedSupport调用AdvisorChainFactory获取一个由MethodIntercept组成的列表,
  2. 调用的时候,将所需要的对象封装为MethodInvocation,做调用。这个调用主要是针对上一步封装的好的MethodIntercept的集合。

带着总纲,开始代码分析

getProxy方法分析

获取代理对象,本质是通过Proxy来创建代理对象,通过Proxy来创建代理对象的时候,需要传递要实现的接口和InvocationHandler.

InvocationHandler就是当前的JdkDynamicAopProxy。

所以,下面的代码的主要功能就是确定接口,并且通过Proxy来创建代理对象。

通过AopProxyUtils.completeProxiedInterfaces(this.advised, true)来确定要实现的接口。这个方法里面通过传递进来的AdvisedSupport来确定接口,并且按照情况添加几个特殊的接口,(SpringProxy,Advised,DecoratingProxy)。AdvisedSupport里面接口的来源就是通过ProxyFactory添加进来的。

并且在findDefinedEqualsAndHashCodeMethods方法设置hashcode和equals的标志位。

问题

  1. 为啥要添加那几个特殊的接口?
  2. 为啥要单独的找到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方法。

扫描二维码关注公众号,回复: 13555427 查看本文章

方法的主要作用在一开始的时候已经交代清楚了,主要就是构建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接口,否则就不会。通过这个接口会拿到被代理类的类对象。

问题

  1. 下面对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

代码主要流程如下:

  1. 遍历Advisor(从Advised中获取到的)

  2. 在遍历的时候判断不同的情况。

    1. PointcutAdvisor(这个处理就比较复杂)

      拿到他的切入点里面的ClassFilter做匹配。在后去joinPoint里面的MethodMatch做匹配。如果MethodMatch是IntroductionAwareMethodMatcher,就会从Advisor中判断是否有IntroductionAdvisor。

      在两者都满足的情况下,通过适配器做适配,添加到结果里面返回。

    2. IntroductionAdvisor

      只是通过ClassFilter来做匹配,这个默认值是True。通过适配器做适配,添加到结果里面返回

    3. 其他

      剩下的直接通过适配器做适配。添加到结果返回

  3. 如果适配器里面没有找到,就会报错。

问题?

  1. 适配器长什么样子?

image-20211212134703062.png

适配器是要提供了两个方法。

  1. Support,这个本质是instanceof来判断
  2. getInterceptor,本质就是获取Advisor里面的Advice,然后自己包装了一下,创建对应的MethodIntercept。
  1. 为啥对于MethodMatch为IntroductionAwareMethodMatcher要做处理,并且hasMatchingIntroductions方法里面是做了什么事情?

    IntroductionAwareMethodMatcher继承了MethodMatcher,在匹配的时候考虑到了Introductions。具体的判断就要在子类里面去判断了。

    hasMatchingIntroductions里面主要配置里面的advisors是否有IntroductionAdvisor类型,并且IntroductionAdvisor里面的classFilter是否是满足的。

  2. Advised的isPreFiltered的作用什么什么?因为在做类适配的时候,有两个或关系的条件。它就是第一个。

    设置为true的时候,可以跳过之后的ClassFilter的检查,他们叫做预先过滤。如果跳过的话,其实没有太大的影响,只是跳过了ClassFilter的检查,但是如果Advice不合理,还是会报错。因为适配器里面不支持,直接接报错。

  3. 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

还是从类图开始分析

MethodInvocation.png

根接口是JoinPoint。MethodInvocation继承与Invocation,Invocation继承与JoinPoint。ReflectiveMethodInvocation实现了ProxyMethodInvocation接口,并且ReflectiveMethodInvocation依赖ProxyMethodInvocation。同样的cglibMethodInvocation也是。

这里面重要的方法是proceed(),得在此强调接口的功能呢,接口是用来做功能的,具体的实现的细节是在实现类里面。面向接口编程,可以让主体的功能不受影响。在不同的接口里面添加不同的功能。慢慢的套,具体的功能就出来了。

比如下面我们要分析ReflectiveMethodInvocation,他实现了ProxyMethodInvocation,他在MethodInvocation的基础上,增加了对代理对象的持有。

先看ReflectiveMethodInvocation的构造方法和属性

image-20211212150222625.png

通过构造方法设置属性,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);
   }
}
复制代码

image-20211212152549767.png

从类图来看,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;
   }
}
复制代码

image-20211212155754675.png

代码表现来看,就是这个样子,那么这个在模式在日后是可以直接仿照的,直接写起来。

问题?

  1. 这里有拦截器的顺序吗?

    没有,从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来创建代理对象的方法分析结束了。

补充说明

  1. 添加到ProxyFactory的Advice会被包装为啥?

    有两种,DefaultIntroductionAdvisor和DefaultPointcutAdvisor,如果添加的advice实现了IntroductionInfo接口,就会获取IntroductionInfo#getInterfaces来获取需要引入的接口,添加到DefaultIntroductionAdvisor的interfaces里面。

  2. 之前说IntroductionInfo接口,可以添加接口。上面没有添加接口的操作,接口是在哪里添加的?

    通过IntroductionInfo添加接口,理论上来说,必须在创建代理对象的时候将接口确定好,所以,这个添加操作必须是在这个前面,在添加advice的时候添加的。对于实现了IntroductionInfo的接口,会构建DefaultIntroductionAdvisor,然后会在 AdvisedSupport#validateIntroductionAdvisor里面会添加到interfaces里面去。

到此,结束了。

关于博客这件事,我是把它当做我的笔记,里面有很多的内容反映了我思考的过程,因为思维有限,不免有些内容有出入,如果有问题,欢迎指出。一同探讨。谢谢。

猜你喜欢

转载自juejin.im/post/7040791198553882661