Spring AOP 之动态代理的创建

本文旨在理清Spring的动态代理创建过程中各个组件类如何协作,共同完成代理的设置和创建,不考虑过多细节问题。

使用示例

话不多说,先上代码:

// before Advice
public class GreetingAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        String client = (String)args[0];
        System.out.println("Nice to meet you ! " + client + ". ");
    }
}

// 接口
public interface Waiter {
    void serveTo(String name);
}

//实现类
public class NaiveWaiter implements Waiter {
    @Override
    public void serveTo(String name) {
        System.out.println("serving " + name + "...");
    }
}

//创建代理
 ProxyFactory factory = new ProxyFactory();
 factory.addAdvice(new GreetingAdvice());
 factory.setTarget(new NaiveWaiter());
 Waiter proxy = (Waiter) factory.getProxy();
 proxy.greetTo("Mr. Zhang");

//result
Nice to meet you ! Mr. Zhang. 
greet to Mr. Zhang...

可以看到,NaiveWaiter已经被BeforeAdvice增强了,我们来看ProxyFactory如何实现增强的。

从ProxyFactory开始

从上面的示例可以看出,ProxyFactory.getProxy()直接就创建了代理。我们跟踪一下源码,看看其调用过程,得到如下时序图:
image.png
可以看到,ProxyFactory创建代理时,先获取到AopProxyFactory,并将代理的创建工作委托给AopProxyFactory。具体的AopProxyFactory通过调用getProxy方法来来创建代理。
这里涉及到三个核心类:ProxyFactory,AopProxyFactory以及JdkDynamicAopProxy。我们来逐个分析。

ProxyFactory

ProxyFactory的功能就是创建Proxy.我们先看看他的继承结构:
image.png

  • ProxyConfig: Spring创建代理的过程中需要访问的全局配置信息。主要包含一下四个:
    1. proxyTargetClass:是否强制代理目标类。Spring在对bean进行自动创建代理时,会对bean进行分析,如果bean实现了某些接口,则使用JDK动态代理,如果目标类没有实现接口,则使用cglib动态代理创建目标类的子类。proxyTargetClass为true时,则上述的默认判断,直接使用cglib动态代理。
    2. exposeProxy:是否在线程上下文中暴露代理对象(专题讲解)。
    3. opaque:是否隐藏代理的Advised方法。opaque为false,则Spring创建的代理类都实现Advised接口,如果为true,则不实现该接口。
    4. optimize:如果使用cglib创建代理,是否在创建代理时进行激进的优化。默认为false.
  • TargetClassAware: 规定获取代理对象的类型的方法
  • Advised: 这个接口主要规定了创建代理的过程中以及代理创建后,访问相关AOP配置信息的方法,也就是说一个具体的被代理的对象上能够访问到的代理相关信息。比如规定了如何获取Adivce、Adivsor、Interceptor以及代理类实现的接口等。代理工厂需要在代理创建的过程中访问这些信息,代理对象的使用者也可以将从代理对象中获取这些信息。Spring当中创建的每个代理对象都是该接口的实现类(opaque为true的除外),以便访问代理对象的相关代理信息。
  • AdvisedSupport: 代理的相关配置管理的基类。既有ProxyConfig指定的全局配置管理能力,也有Advised指定的对于每个代理的相关配置的管理能力。
  • ProxyCreatorSupport:代理工厂的基类。Spring的设计是将代理的创建委托给AopProxy。这个类实现了如何获取AopProxy这个模板方法,其所有子类代理工厂都可以通过这个方法获取AopProxy,进而使用AopProxy创建代理。而如何获取AopProxy呢?ProxyCreatorSupport又将其委托给了AopProxyFactory,只要为ProxyCreatorSupport配置好了AopProxyFactory,它就可以按照既定流程(调用AopProxyFactory的getAopProxy方法)自动获取了。

分析完了类的职责以及他们如何协作的,我们再来看看JdkDynamicAopProxy是如何创建代理的:

@Override
	public Object getProxy() {
		return getProxy(ClassUtils.getDefaultClassLoader());
	}

	@Override
	public Object getProxy(@Nullable ClassLoader classLoader) {
		if (logger.isTraceEnabled()) {
			logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
		}
		return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
	}

可以看到,JdkDynamicAopProxy在创建代理的时候,使用的interface数组是proxiedInterfaces,传入的InvocationHandler是它自己的实例对象(this)。
我们按图索骥,先看proxiedInterfaces是从哪里来的,再看JdkDynamicAopProxy如何实现的InvocationHandler接口。
proxiedInterfaces的来源:

public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
		Assert.notNull(config, "AdvisedSupport must not be null");
		if (config.getAdvisorCount() == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
			throw new AopConfigException("No advisors and no TargetSource specified");
		}
		this.advised = config;
		this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces);
	}

在JdkDynamicAopProxy的构造方法中就已经完成了proxiedInterfaces的初始化,再来看详情:

static Class<?>[] completeProxiedInterfaces(AdvisedSupport advised, boolean decoratingProxy) {
		Class<?>[] specifiedInterfaces = advised.getProxiedInterfaces();
		if (specifiedInterfaces.length == 0) {
			// No user-specified interfaces: check whether target class is an interface.
			Class<?> targetClass = advised.getTargetClass();
			if (targetClass != null) {
				if (targetClass.isInterface()) {
					advised.setInterfaces(targetClass);
				}
				else if (Proxy.isProxyClass(targetClass) || isLambda(targetClass)) {
					advised.setInterfaces(targetClass.getInterfaces());
				}
				specifiedInterfaces = advised.getProxiedInterfaces();
			}
		}
		List<Class<?>> proxiedInterfaces = new ArrayList<>(specifiedInterfaces.length + 3);
		for (Class<?> ifc : specifiedInterfaces) {
			// Only non-sealed interfaces are actually eligible for JDK proxying (on JDK 17)
			if (isSealedMethod == null || Boolean.FALSE.equals(ReflectionUtils.invokeMethod(isSealedMethod, ifc))) {
				proxiedInterfaces.add(ifc);
			}
		}
		if (!advised.isInterfaceProxied(SpringProxy.class)) {
			proxiedInterfaces.add(SpringProxy.class);
		}
		if (!advised.isOpaque() && !advised.isInterfaceProxied(Advised.class)) {
			proxiedInterfaces.add(Advised.class);
		}
		if (decoratingProxy && !advised.isInterfaceProxied(DecoratingProxy.class)) {
			proxiedInterfaces.add(DecoratingProxy.class);
		}
		return ClassUtils.toClassArray(proxiedInterfaces);
	}

JdkDynamicAopProxy中的proxiedInterfaces是从Advised的interfaces中加工而来。加工步骤如下:

  1. 如果interfaces是空的,则分析目标类,如果目标类是接口,那么将目标类当做要实现的接口添加到proxiedInterfaces中,如果目标类是个代理或Lambda,则将目标类上的所有接口都添加到proxiedInterfaces
  2. 将SpringProxy添加到proxiedInterfaces
  3. 如果没有设置隐藏代理信息,则将Advised添加到proxiedInterfaces

Advised的interfaces从何而来?

  1. 用户可以使用ProxyFactory直接添加接口
  2. Spring对其中的bean创建代理时,会对bean的类型进行分析,从类型中分析而来。读者可以看AbstractAutoProxyCreator代码。

再看JdkDynamicAopProxy对InvocationHandler的实现方法:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Object oldProxy = null;
		boolean setProxyContext = false;

		TargetSource targetSource = this.advised.targetSource;
		Object target = null;

		try {
			if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
				// The target does not implement the equals(Object) method itself.
				return equals(args[0]);
			}
			else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
				// The target does not implement the hashCode() method itself.
				return hashCode();
			}
			else if (method.getDeclaringClass() == DecoratingProxy.class) {
				// There is only getDecoratedClass() declared -> dispatch to proxy config.
				return AopProxyUtils.ultimateTargetClass(this.advised);
			}
			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) {
				// Make invocation available if necessary.
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			// Get as late as possible to minimize the time we "own" the target,
			// in case it comes from a pool.
			target = targetSource.getTarget();
			Class<?> targetClass = (target != null ? target.getClass() : null);

			// Get the interception chain for this method.
			List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

			// Check whether we have any advice. If we don't, we can fallback on direct
			// reflective invocation of the target, and avoid creating a MethodInvocation.
			if (chain.isEmpty()) {
				// We can skip creating a MethodInvocation: just invoke the target directly
				// Note that the final invoker must be an InvokerInterceptor so we know it does
				// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
				Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
			}
			else {
				// 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();
			}

			// Massage return value if necessary.
			Class<?> returnType = method.getReturnType();
			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 {
			if (target != null && !targetSource.isStatic()) {
				// Must have come from TargetSource.
				targetSource.releaseTarget(target);
			}
			if (setProxyContext) {
				// Restore old proxy.
				AopContext.setCurrentProxy(oldProxy);
			}
		}
	}

可以看到:

  1. 如果代理的目标类没有自己实现equals()hashcode() 方法,则直接调用Spring为代理类提供的equalshashcode方法
  2. 如果调用的是DecoratingProxy中的getDecoratedClass()和Advised接口中定义的方法,则调用Spring提供的方法。
  3. 如果不是以上两种,则通过Adivsed构造出应用到目标方法的过滤器链(MethodInteceptor数组),使用MehtodInvocation应用到方法上。
  4. MehtodInvocation是一个典型的责任链,每一个MethodInteceptor方法的invoke调用完之后再执行下一个。

以上重点是搞清楚步骤3中如何构造出过滤器链,以及步骤4中过滤器链的应用过程。
我们看getInterceptorsAndDynamicInterceptionAdvice方法:

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, @Nullable Class<?> targetClass) {
		MethodCacheKey cacheKey = new MethodCacheKey(method);
		List<Object> cached = this.methodCache.get(cacheKey);
		if (cached == null) {
			cached = this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
					this, method, targetClass);
			this.methodCache.put(cacheKey, cached);
		}
		return cached;
	}

AdvisedSupport中的代码只是实现了缓存作用,真正的构造逻辑在advisorChainFactory中:

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
			Advised config, Method method, @Nullable Class<?> targetClass) {

		// This is somewhat tricky... We have to process introductions first,
		// but we need to preserve order in the ultimate list.
		AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
		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) {
			if (advisor instanceof PointcutAdvisor) {
				// Add it conditionally.
				PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
				if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {
					MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
					boolean match;
					if (mm instanceof IntroductionAwareMethodMatcher) {
						if (hasIntroductions == null) {
							hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
						}
						match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
					}
					else {
						match = mm.matches(method, actualClass);
					}
					if (match) {
						MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
						if (mm.isRuntime()) {
							// Creating a new object instance in the getInterceptors() method
							// isn't a problem as we normally cache created chains.
							for (MethodInterceptor interceptor : interceptors) {
								interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
							}
						}
						else {
							interceptorList.addAll(Arrays.asList(interceptors));
						}
					}
				}
			}
			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;
	}

该方法的基本逻辑是根据Advised中Adivsor信息获取Advice和Pointcut(如果是PointcutAdvisor的话),如果Pointcut匹配当前正在调用的方法,则将Advice转化为MethodInceptor加入到拦截器链。具体的Piontcut匹配这里不在详说。Advice到MethodIntceptor的转化则是使用AdvisorAdaptor实现的。
拦截器链的执行过程如下图:
绘图1.png

posted @ 2023-01-31 15:13  小张同学哈  阅读(274)  评论(0)    收藏  举报