欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-11-30 13:42:38
代理模式Jdk动态代理通过反射机制,创建一个代理类对象实例并返回。用户进行方法调用时使用创建代理对象时,需要传递该业务类的类加载器(用来获取业务实现类的元数据,在包装方法是调用真正的业务方法)、接口、handler实现类,被代理的对象必须实现了某一个接口。public T getProxy(T target) {return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.ge...

代理模式

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