当前位置: 移动技术网 > IT编程>开发语言>Java > Spring中的两种代理模式的实现原理

Spring中的两种代理模式的实现原理

2020年07月07日  | 移动技术网IT编程  | 我要评论

代理模式

Jdk动态代理

通过反射机制,创建一个代理类对象实例并返回。用户进行方法调用时使用创建代理对象时,需要传递该业务类的类加载器(用来获取业务实现类的元数据,在包装方法是调用真正的业务方法)、接口、handler实现类,被代理的对象必须实现了某一个接口。

public <T> T getProxy(T target) {
		return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
				target.getClass().getInterfaces(), new MyInvocationHandler(
						target));
	}

CGLIB动态代理

一个java字节码的生成工具,它动态生成一个被代理类的子类,子类重写被代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑,通过增强器Enhancer和拦截器MethodInterceptor去实现。
创建代理的步骤:
生成代理类的二进制字节码文件;
加载二进制字节码,生成Class对象;
通过反射机制获得实例构造,并创建代理类对象

public static <T> T getObjectProxy(T target, Callback[] callbacks,
			CallbackFilter filter) {
		// 1、创建加强器用来创建动态代理
		Enhancer enhancer = new Enhancer();
		// 2 、为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
		enhancer.setSuperclass(target.getClass());
		// 3、 设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
		// 3.1callback用于类的所有方法进行加强后返回
		// enhancer.setCallback(callback);
		// 3.2callbacks对指定的方法进行拦截,需要配置拦截链CallbackFilter
		enhancer.setCallbacks(callbacks);
		enhancer.setCallbackFilter(filter);
		// 4、创建动态代理类对象并返回
		return (T) enhancer.create();
	}

Spring创建动态代理

AbstractAutoProxyCreator根据配置获取到所有可以引用bean的增强器List以后,就需要为bean创建代理,Spring代理又分为JDK动态代理和CGLIB动态代理两种方式,所以按照常规方式,需要一个ProxyFactory去根据需求创建具体的Proxy对象,Spring也是这么去做的。

protected Object createProxy(
			Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

		if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
			AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
		}
	  	//1、获取当前类钟的属性
		ProxyFactory proxyFactory = new ProxyFactory();
		proxyFactory.copyFrom(this);
		//2、添加代理接口
		if (!proxyFactory.isProxyTargetClass()) {
			if (shouldProxyTargetClass(beanClass, beanName)) {
				proxyFactory.setProxyTargetClass(true);
			}
			else {
				evaluateProxyInterfaces(beanClass, proxyFactory);
			}
		}
		//3、封装Advisor到ProxyFactory中
		Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
		for (Advisor advisor : advisors) {
			proxyFactory.addAdvisor(advisor);
		}
		//4、设置要代理的类
		proxyFactory.setTargetSource(targetSource);
		//5、定制代理,子类扩展方法
		customizeProxyFactory(proxyFactory);

		proxyFactory.setFrozen(this.freezeProxy);
		if (advisorsPreFiltered()) {
			proxyFactory.setPreFiltered(true);
		}
		//6、进行获取代理
		return proxyFactory.getProxy(getProxyClassLoader());
	}

创建代理的过程

public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
		if (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)) {
				return new JdkDynamicAopProxy(config);
			}
			return new ObjenesisCglibAopProxy(config);
		}
		else {
			return new JdkDynamicAopProxy(config);
		}
	}

optimize:默认false,用来控制通过CGLIB创建的代理是否使用激进的优化策略。除非完全了解AOP代理如何处理优化,否则不推荐使用这个设置,目前这个属性仅仅用于CGLIB代理,JDK动态代理无效。
proxyTargetClass:默认false,<aop:aspectj-autoproxy />有一个proxy-target-class属性,当该值被设置为true时,CGLIB代理将被创建。
hasNoUserSuppliedProxyInterfaces:是否存在代理接口,代理目标没有实现接口,或者仅有一个SpringProxy接口时返回true。

CGLIB与动态代理的区别:
1、JDK动态代理只能对实现了接口的类生成代理,而不能针对类。
2、CGLIB是针对类实现代理,主要是针对指定类生成一个子类,覆盖其中的方法,因为是继承关系,所欲该类或刚发最好不要声明成final(不能被子类继承)。

无论是ObjenesisCglibAopProxy还是JdkDynamicAopProxy都实现了AopProxy,都有一个AdvisedSupport,也是前面已经初始化过的ProxyFactory。

JdkDynamicAopProxy

实现了AopProxy,同时InvocationHandler接口,核心方法就两个:
getProxy:获取代理
invoke:目标方法在调用时进行拦截。

public Object getProxy(ClassLoader classLoader) {
		if (logger.isDebugEnabled()) {
			logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
		}
		Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised);
		findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}
被代理的类自调用

SpringAop在运行过程中通过动态代理进入到invoke,然后判断是否需要进行方法拦截,需要的时候才会根据配置生成拦截器链。但自调用是没有代理对象的,在spring的事物处理中,有时候出现类似的问题,我们可以通过<aop:aspectj-autoproxy expose-proxy=“true”/>在自调用的时候将代理对象暴露出来。
原案例:

public String getStr(String str) {
		clear();
		return str;
	}
public class TestAop {
	public static void main(String[] args) {
		ApplicationContext ctx = new ClassPathXmlApplicationContext("META-INF/aop/spring-aop.xml");
		TestC c = ctx.getBean(TestC.class);
		c.getStr("test");
	}
	// 配置 <aop:aspectj-autoproxy expose-proxy="false"/>

结果:
Constructor of TestC
LogBean#beforePoint

使用<aop:aspectj-autoproxy expose-proxy=“true”/>

public String getStr() {
		TestC testC = (TestC)AopContext.currentProxy();
		testC.clear();
		return str;
	}

结果:
Constructor of TestC
LogBean#beforePoint
LogBean#beforePoint

expose-proxy="true"是在什么时候通过什么样的方式将自调用的代理给暴露出来的,其原理就在JdkDynamicAopProxy的invoke方法中,通过判断expose-proxy的值,是否将当前对象的代理给暴露出来。

// 其余代码省略
if (this.advised.exposeProxy) {
				// Make invocation available if necessary.
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}
// 其余代码省略
if (setProxyContext) {
				// Restore old proxy.
				AopContext.setCurrentProxy(oldProxy);
			}

至于invoke方法中,关于如何将增强器封装成拦截器链,然后通过递归去实现责任链的方式就不仔细详解,想知道具体的方法,可以去看DefaultAdvisorChainFactory关于这一段的实现。

// We need to create a method invocation...
invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
// Proceed to the joinpoint through the interceptor chain.
retVal = invocation.proceed();

CglibAopProxy

与JDK动态代理不同的是,CglibAopProxy除了实现AopProxy并没实现其它特别接口。是通过MethodInterceptor来实现方法的拦截,与jdk动态代理处理的方式一模一样。
包含的步骤:
1、是否需要创建代理;
2、Object类的方法处理;
3、expose-proxy="true"的处理;
4、将Advisor封装成ReflectiveMethodInvocation(拦截器链)对代理对象进行处理。

CglibAopProxy的getCallbacks方法中最核心的一段代码:

Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
// ... 省略
Callback[] mainCallbacks = new Callback[] {
				aopInterceptor,  // for normal advice
				targetInterceptor,  // invoke target without considering advice, if optimized
				new SerializableNoOp(),  // no override for methods mapped to this
				targetDispatcher, this.advisedDispatcher,
				new EqualsInterceptor(this.advised),
				new HashCodeInterceptor(this.advised)
		};

进入DynamicAdvisedInterceptor能感受到其本质与jdk动态代理是一致的。

private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {

		private final AdvisedSupport advised;

		public DynamicAdvisedInterceptor(AdvisedSupport advised) {
			this.advised = advised;
		}

		@Override
		public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
			Object oldProxy = null;
			boolean setProxyContext = false;
			Class<?> targetClass = null;
			Object target = null;
			try {
				if (this.advised.exposeProxy) {
					// Make invocation available if necessary.
					oldProxy = AopContext.setCurrentProxy(proxy);
					setProxyContext = true;
				}
				// May be null. Get as late as possible to minimize the time we
				// "own" the target, in case it comes from a pool...
				target = getTarget();
				if (target != null) {
					targetClass = target.getClass();
				}
				List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
				Object retVal;
				// Check whether we only have one InvokerInterceptor: that is,
				// no real advice, but just reflective invocation of the target.
				if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
					// 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 = methodProxy.invoke(target, argsToUse);
				}
				else {
					// We need to create a method invocation...
					retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
				}
				retVal = processReturnType(proxy, target, method, retVal);
				return retVal;
			}
			finally {
				if (target != null) {
					releaseTarget(target);
				}
				if (setProxyContext) {
					// Restore old proxy.
					AopContext.setCurrentProxy(oldProxy);
				}
			}
		}

		@Override
		public boolean equals(Object other) {
			return (this == other ||
					(other instanceof DynamicAdvisedInterceptor &&
							this.advised.equals(((DynamicAdvisedInterceptor) other).advised)));
		}

		/**
		 * CGLIB uses this to drive proxy creation.
		 */
		@Override
		public int hashCode() {
			return this.advised.hashCode();
		}

		protected Object getTarget() throws Exception {
			return this.advised.getTargetSource().getTarget();
		}

		protected void releaseTarget(Object target) throws Exception {
			this.advised.getTargetSource().releaseTarget(target);
		}
	}

JDK动态代理使用的ReflectiveMethodInvocation去处理,CglibAopProxy使用CglibMethodInvocation去处理,CglibMethodInvocation与ReflectiveMethodInvocation是父类与子类的关系,两者都是用同样的过程去处理bean调用时的方法。

无论是JDK动态代理还是CGLIB动态代理,都无法对private方法进行处理,所以private修饰的方法是没有AOP机制的。对于JDK动态代理是基于接口去做代理,接口的方法都是public 修饰的;CGLIB虽然是基于类去生成代理,private与final一样,都是无法被子类继承的。

本文地址:https://blog.csdn.net/zhouyu5408/article/details/107158121

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网