Spring读源码系列之AOP--06---AopProxy===>spring使用jdk和cglib生成代理对象的终极奥义

Posted 大忽悠爱忽悠

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring读源码系列之AOP--06---AopProxy===>spring使用jdk和cglib生成代理对象的终极奥义相关的知识,希望对你有一定的参考价值。

Spring读源码系列之AOP--06---AopProxy===>spring使用jdk和cglib生成代理对象的终极奥义


引子

回看生成DefaultAopProxyFactory的createAopProxy方法来生成代理类

public class DefaultAopProxyFactory implements AopProxyFactory, Serializable 


	@Override
	public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException 
		if (!NativeDetector.inNativeImage() &&
				(config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) 
			Class<?> targetClass = config.getTargetClass();
			if (targetClass == null) 
				throw new AopConfigException("TargetSource cannot determine target class: " +
						"Either an interface or a target is required for proxy creation.");
			
			if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) 
			//采用JdkDynamicAopProxy生成jdk动态代理对象
				return new JdkDynamicAopProxy(config);
			
			//采用ObjenesisCglibAopProxy生成cglib动态代理对象
			return new ObjenesisCglibAopProxy(config);
		
		else 
			return new JdkDynamicAopProxy(config);
		
	

	/**
	 * Determine whether the supplied @link AdvisedSupport has only the
	 * @link org.springframework.aop.SpringProxy interface specified
	 * (or no proxy interfaces specified at all).
	 */
	private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) 
		Class<?>[] ifcs = config.getProxiedInterfaces();
		return (ifcs.length == 0 || (ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0])));
	



Spring所有的代理AopProxy的创建最后都是ProxyCreatorSupport#createAopProxy这个方法

	protected final synchronized AopProxy createAopProxy() 
		if (!this.active) 
			activate();
		
		//getAopProxyFactory拿到的就是DefaultAopProxyFactory 
		//createAopProxy调用的就是DefaultAopProxyFactory类里面的方法
		return getAopProxyFactory().createAopProxy(this);
	

显然它又是调用了AopProxyFactory#createAopProxy方法,它的唯一实现为DefaultAopProxyFactory。

它做了一个简单的逻辑判断:若实现类接口,使用JdkDynamicAopProxy最终去创建,否则交给ObjenesisCglibAopProxy。

最终拿到AopProxy后,调用AopProxy#getProxy()就会拿到这个代理对象,从而进行相应的工作了。

我们基本有一共共识就是:默认情况下,若我们实现了接口,就实用JDK动态代理,若没有就实用CGLIB。那么就下来,就具体看看关乎到代理对象的创建、执行的一个具体过程原理


动态代理和静态代理回顾

AOP(Aspect Orient Programming),一般称为面向切面编程,作为面向对象的一种补充,用于处理系统中分布于各个模块的横切关注点,比如事务管理、日志、缓存等等。

AOP代理主要分为静态代理和动态代理,静态代理的代表为AspectJ;而动态代理则以Spring AOP为代表。静态代理是编译期实现,动态代理是运行期实现,可想而知前者拥有更好的性能。

静态代理:

静态代理是编译阶段生成AOP代理类,也就是说生成的字节码就织入了增强后的AOP对象;(并不会创建出多余的对象)

实现方式:

  • 包装器模式:持有目标对象的引用,然后实际上是调用目标对象的方法。 这种方式也可称为代理模式,但是有明显的缺点(比如一般都需要实现同一个接口,且它是以编码的方式去实现的,侵入性高)
  • AspectJ静态代理方式:非常非常强大。Aspectj并不是动态的在运行时生成代理类,而是在编译的时候就植入代码到class文件。由于是静态织入的,所以性能相对来说比较好。Aspectj不受类的特殊限制,不管方法是private、或者static、或者final的,都可以代理,Aspectj不会代理除了限定方法之外任何其他诸如toString(),clone()等方法,唯一缺点就是必须有AspectJ自己的编译器的支持,所以其实很少使用 Spring也是提供了相关类支持的,比如:LoadTimeWeaverAwareProcessor

基于AspectJ的静态代理方式非常强大,但是它依赖于它自己的编译器。并且还有自己的个性化语言,使用起来不够方便,因此其实还是使用得较少的。主要还是以动态代理为主~~~

动态代理:

动态代理则不会修改字节码,而是在内存中临时生成一个AOP对象,这个AOP对象包含了目标对象的全部方法,并且在特定的切点做了增强处理,并回调原对象的方法

这在我们平时使用中得到了大量的使用,因为使用简单并且还非常灵活,下面就重点介绍。


AopProxy:Aop代理接口

它是一个AOP代理的抽象接口。提供了两个方法,让我们可以获取对应 配置的AOP对象的代理:

public interface AopProxy 
	//Create a new proxy object. Uses the AopProxy's default class loader  ClassUtils.getDefaultClassLoader()
	Object getProxy();
	Object getProxy(@Nullable ClassLoader classLoader);

它的继承关系也很简单,就是接下来我们要说的那几个


JdkDynamicAopProxy—jdk生成动态代理对象

final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable 

	/** use serialVersionUID from Spring 1.2 for interoperability. */
	private static final long serialVersionUID = 5531744639992436476L;

	/** We use a static Log to avoid serialization issues. */
	private static final Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);

	/**当前代理对象相关配置信息保存 **/
	private final AdvisedSupport advised;
    //当前代理对象需要代理的接口
	private final Class<?>[] proxiedInterfaces;

	/**
	 * Is the @link #equals method defined on the proxied interfaces?
	 */
	private boolean equalsDefined;

	/**
	 * Is the @link #hashCode method defined on the proxied interfaces?
	 */
	private boolean hashCodeDefined;

	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;
		//completeProxiedInterfaces方法会获得当前类需要代理的接口
		//并且额外还会让代理对象实现两个接口,但是因为第二个参数为true
		//因此一共会让代理对象再额外实现三个接口:SpringProxy,Advised和DecoratingProxy
		this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
		findDefinedEqualsAndHashCodeMethods(this.proxiedInterfaces);
	


	@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());
		
		//获得代理对象的方法十分的简单,就是最原始的jdk生成动态代理的方法
		//但是此时传入的拦截器方法为当前类,因为其实现了InvocationHandler接口
		return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
	

	/**
	 * Finds any @link #equals or @link #hashCode method that may be defined
	 * on the supplied set of interfaces.
	 * @param proxiedInterfaces the interfaces to introspect
	 */
	private void findDefinedEqualsAndHashCodeMethods(Class<?>[] proxiedInterfaces) 
		for (Class<?> proxiedInterface : proxiedInterfaces) 
			Method[] methods = proxiedInterface.getDeclaredMethods();
			for (Method method : methods) 
				if (AopUtils.isEqualsMethod(method)) 
					this.equalsDefined = true;
				
				if (AopUtils.isHashCodeMethod(method)) 
					this.hashCodeDefined = true;
				
				if (this.equalsDefined && this.hashCodeDefined) 
					return;
				
			
		
	


	/**
调用链的执行
	 */
	@Override
	@Nullable
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable 
		Object oldProxy = null;
		boolean setProxyContext = false;
        //拿到包装了目标对象的TargetSource 
		TargetSource targetSource = this.advised.targetSource;
		Object target = null;

		try 
             //“通常情况”Spring AOP不会对equals、hashCode方法进行拦截增强,所以此处做了处理
			// equalsDefined为false(表示自己没有定义过equals方法)  那就交给代理去比较
			// hashCode同理,只要你自己没有实现过此方法,那就交给代理吧
			// 需要注意的是:这里统一指的是,如果接口上有此方法,但是你自己并没有实现equals和hashCode方法,那就走AOP这里的实现
			// 如国接口上没有定义此方法,只是实现类里自己@Override了HashCode,那是无效的,就是普通执行吧
			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();
			
			//如果当前方法属于DecoratingProxy接口中getDecoratedClass方法
			else if (method.getDeclaringClass() == DecoratingProxy.class) 
				// There is only getDecoratedClass() declared -> dispatch to proxy config.
				//那么直接通过分析代理配置信息返回目标对象的类型
				return AopProxyUtils.ultimateTargetClass(this.advised);
			
			//opaque默认为false,表示代理对象会继承Advised接口
			else if (!this.advised.opaque && 
			        //当前方法属于某个接口中的方法
			        method.getDeclaringClass().isInterface() &&
					//当前接口是Advised或者其子类
					method.getDeclaringClass().isAssignableFrom(Advised.class)) 
				// Service invocations on ProxyConfig with the proxy config...
				//那么当前用户是想将代理对象强制转换为advised接口后,调用该接口中某个方法
				//那么这里转而调用内部维护的代理类配置信息的method方法
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			

			Object retVal;
           
             //是否暴露代理对象,默认false可配置为true,如果暴露就意味着允许在线程内共享代理对象,
			//注意这是在线程内,也就是说同一线程的任意地方都能通过AopContext获取该代理对象,这应该算是比较高级一点的用法了。
			// 这里缓存一份代理对象在oldProxy里~~~后面有用
			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.
			//对代理类配置信息中的Advisors集合进行筛选,找出可以应用到当前类和当前类的method方法上的增强器
			//然后把这些增强器适配为methodInterceptor后加入集合中统一返回
			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.
				//拦截器链开始执行
				//ReflectiveMethodInvocation的proceed方法,不清楚的可以翻看我之前的文章
				//该方法最后返回方法的执行结果
				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;
			
			//如果方法实际返回值不为null,但是获得的返回结果为null,那么抛出异常
			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绑定的target对象,这样一来,targetSource可以完成重用111
				targetSource.releaseTarget(target);
			
			if (setProxyContext) 
				// 把老的代理对象重新set进去~~~
				AopContext.setCurrentProxy(oldProxy);
			
		
	


	/**
	 * Equality means interfaces, advisors and TargetSource are equal.
	 * <p>The compared object may be a JdkDynamicAopProxy instance itself
	 * or a dynamic proxy wrapping a JdkDynamicAopProxy instance.
	 */
	@Override
	public boolean equals(@Nullable Object other) 
		if (other == this) 
			return true;
		
		if (other == null) 
			return false;
		

		JdkDynamicAopProxy otherProxy;
		if (other instanceof JdkDynamicAopProxy) 
			otherProxy = (JdkDynamicAopProxy) other;
		
		else if (Proxy.isProxyClass(other.getClass())) 
			InvocationHandler ih = Proxy.getInvocationHandler(other);
			if (!(ih instanceof JdkDynamicAopProxy)) 
				return false;
			
			otherProxy = (JdkDynamicAopProxy) ih;
		
		else 
			// Not a valid comparison...
			return false;
		

		// If we get here, otherProxy is the other AopProxy.
		return AopProxyUtils.equalsInProxy(this.advised, otherProxy.advised);
	

	/**
	 * Proxy uses the hash code of the TargetSource.
	 */
	@Override
	public int hashCode() 
		return JdkDynamicAopProxy.class.hashCode() * 13 + this.advised.getTargetSource().hashCode();
	



该类的设计思路和我们平时自己使用jdk动态代理思路一致,大致有以下几步:

  • 通过构造函数传入的AdvisedSupport代理配置信息,分析得到当前代理类需要实现的接口数组
  • 获取代理对象的时候,就和平时一样,通过newProxyInstance完成即可,但是InvocationHandler传入的是JdkDynamicAopProxy自身,因为他实现了该接口
  • 当目标方法被调用,会触发InvocationHandler的invoke方法,在该方法内JdkDynamicAopProxy会首先获取拦截器链,然后执行拦截器链,获取最终执行结果,然后返回

对jdk动态代理底层原理不清楚的,建议看一下本文

还有一点需要注意,就是代理类会额外实现SpringProxy,Advised和DecoratingProxy三个接口,代码体现在下面这行:

this.proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);

AopProxyUtils不懂的,看本文


CglibAopProxy----cglib生成动态代理对象

要想搞懂CglibAopProxy的组织思路,callbackFilter必须要了解,建议大家先看一下:

cglib的callbackFilter介绍

@SuppressWarnings("serial")
class CglibAopProxy implements AopProxy, Serializable 

	// Constants for CGLIB callback array indices
	//这里的序号用于callBackFilter中,选择某个方法交给某个callback时,会用到
	private static final int AOP_PROXY = 0;
	private static final int INVOKE_TARGET = 1;
	private static final int NO_OVERRIDE = 2;
	private static final int DISPATCH_TARGET = 3;
	private static final int DISPATCH_ADVISED = 4;
	private static final int INVOKE_EQUALS = 5;
	private static final int INVOKE_HASHCODE = 6;


	/** Logger available to subclasses; static to optimize serialization. */
	protected static final Log logger = LogFactory.getLog(CglibAopProxy.class);

	/** Keeps track of the Classes that we have validated for final methods. */
	//对已经校验过的类进行缓存处理
	private static final Map<Class<?>, Boolean> validatedClasses = new WeakHashMap<>();


	/**  代理配置类信息 **/
	protected final AdvisedSupport advised;
   
   //因为是采用继承方式实现的代理,因此需要考虑父类的构造函数
	@Nullable
	protected Object[] constructorArgs;
   //构造函数参类型
	@Nullable
	protected Class以上是关于Spring读源码系列之AOP--06---AopProxy===>spring使用jdk和cglib生成代理对象的终极奥义的主要内容,如果未能解决你的问题,请参考以下文章

Spring读源码系列之AOP--02---aop基本概念扫盲---下

Spring读源码系列之AOP--03---aop底层基础类学习

Spring读源码系列之AOP--08--aop执行完整源码流程之自动代理创建器导入的两种方式

Spring读源码系列之AOP--05---aop常用工具类学习

Spring读源码系列之AOP--07---aop自动代理创建器(拿下AOP的最后一击)

Spring读源码系列之AOP--06---AopProxy===>spring使用jdk和cglib生成代理对象的终极奥义