Spring的代理模式及Spring AOP-JDKDynamicAopProxy

本文深入探讨Spring框架中的AOP原理及动态代理机制,包括JDK动态代理和CGLIB的区别与应用场景,并通过示例代码展示如何实现动态代理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本博中关于Spring的文章:Spring IOC和AOP原理Spring事务原理探究Spring配置文件属性详解Spring中的代理模式


代理模式:对其他对象提供一种代理以控制对这个对象的访问。代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。代理模式的思想是为了提供额外的处理或者不同的操作而在实际对象与调用者之间插入一个代理对象。

  代理模式中一般涉及4中角色:

InterfaceSubject :该接口是对被访问者或者被访问资源的抽象。

SubjectImpl:被访问者或者被访问资源的具体实现类,实现了InterfaceSubject

SubjectProxy:被访问者或者被访问资源的代理实现类,该类持有一个InterfaceSubject接口的实例

Client:代表访问者的抽象角色,Client将会访问InterfaceSubject类型的对象或者资源,在这个情景中,Client无法直接访问到InterfaceSubject资源,而是通过代理类SubjectProxy去实现。

        静态代理:如果不按照proxy模式的方式进行纯Java的编程,即写代理类对真实的类进行代理处理,就为静态代理。静态代理式的缺点:对于相同的横切逻辑,如果要插入到不同的目标当中的时候,需要重新去实现Proxy代理类。

 动态代理:JDK1.3之后引入了一种称之为动态代理的机制,使用该机制,我们可以指定的接口在系统运行期间动态地生成代理对象,动态代理机制的实现主要有一个类和一个接口组成,即reflect.Proxy reflect.InvocationHandler.  动态代理与静态代理相比较,最大的好处是接口中声明的所有方法都被转移到一个集中的方法中处理(invoke),这样,在接口方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样每一个方法进行中转。 

动态代理类只能代理接口,代理类都需要实现InvocationHandler类,实现invoke方法。该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法返回的值是被代理接口的一个实现类InvocationHandler就是我们实现横切逻辑的地方,作用跟Advice一样。缺陷:动态代理机制只能对实现了相应接口的类使用,因此在默认的情况下,如果对象实现了某个接口,这采用动态代理机制为某个生成代理对象实现,如果没有,这使用一种CGLIB的动态字节码生成类库,为目标生成动态代理实例

即动态代理分为两种,一种为原来的类实现了接口那么就可以使用Java提供的InvocationHandler类进行动态代理,如果原来的类没有实现接口就只能使用CGLib来实现动态代理。

可查看,AutowireCapableBeanFactory源码 ,由于版本不同,源码与下列摘出代码有出入。


Reading the source of Spring AOP. The invoking flow is as the fllowing: 

       A AbstractAutowireCapableBeanFactory.createBean 

     -->bean instanceof BeanFactoryAware

    -->bean.setBeanFactory-----ProxyFactoryBean is the implementation of BeanFactoryAware

     -->ProxyFactoryBean.setBeanFactory().

AbstractAutowireCapableBeanFactory is the subclass of AbstractBeanFacotory

    -->beanFactory.getObjectForSharedInstance()

    -->ProxyFactoryBean.getObject()

    -->AdvisedSupport.createAopProxy()

    -->getAopProxyFactory().createAopProxy()

    -->-DefaultAopProxyFactory is the implenmentation of AopProxyFactory

   -->DefaultAopProxyFactory.createAopProxy()

    -->JdkDynamicAopProxy or CglibProxyFactory.createCglibProxy

   -->JdkDynamicAopProxy implements AopProxy, InvocationHandler

   -->.......

 

//BeanFactory-CreateBean
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory
		implements AutowireCapableBeanFactory {
protected Object createBean(String beanName, RootBeanDefinition mergedBeanDefinition, Object[] args)
			throws BeanCreationException {
populateBean(beanName, mergedBeanDefinition, instanceWrapper);

			if (bean instanceof BeanNameAware) {
				if (logger.isDebugEnabled()) {
					logger.debug("Invoking setBeanName on BeanNameAware bean '" + beanName + "'");
				}
				((BeanNameAware) bean).setBeanName(beanName);
			}

			if (bean instanceof BeanFactoryAware) {
				if (logger.isDebugEnabled()) {
					logger.debug("Invoking setBeanFactory on BeanFactoryAware bean '" + beanName + "'");
				}
				((BeanFactoryAware) bean).setBeanFactory(this);
			}

			originalBean = bean;
			bean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);//before
			invokeInitMethods(beanName, bean, mergedBeanDefinition);//init
			bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);//after
}}


Spring IOC和AOP原理 中讲到的,可知AbstractAutowireCapableBeanFactory 是一个BeanFactory,用于创建Bean,代理类也不例外,其功能从名字上可以看出即是一种自动织入AOP到Bean的Factory。而BeanNameAutoProxyCreator和DefaultAdvisorAutoProxyCreator,这两者都继承了AbstractPoxyCreator,该类implements BeanFactoryAware接口,而DefaultAdvisorAutoProxyCreator还Impements BeanNameAware 接口,但BeanNameAware只支持一个BeanName, 而BeanNameAutoProxyCreator 可以支持String[] 即多个。可以查看BeanNameAutoProxyCreator源码 和 DefaultAdvisorAutoProxyCreator源码

BTW, Spring在线源码库,版本是3.1.  下面代码是ProxyFactoryBean,其也Implements了 BeanFactoryAware,但是在Spring IOC和AOP原理中,知道他只能代理一个类对象。在ProxyFactoryBean的初始化中,根据其XML定义,要配置interfaceNames和配置Intercepters列表,后者可以在setBeanFactory中可以看到,是创建了createAdvisorChain(), 最后借住AdvisedSupport中的getAopProxyFactory生成代理对象。

 

public class ProxyFactoryBean extends AdvisedSupport
    implements FactoryBean, BeanFactoryAware, AdvisedSupportListener {

/**
	 * Set the names of the interfaces we're proxying. If no interface
	 * is given, a CGLIB for the actual class will be created.
	 * <p>Alternatively, use the "interfaces" property of type Class array
	 * (the bean factory will automatically convert from String to Class there).
	 * @see #setInterfaces
	 */
	public void setProxyInterfaces(String[] interfaceNames) throws ClassNotFoundException {
		Class[] interfaces = AopUtils.toInterfaceArray(interfaceNames);
		setInterfaces(interfaces);
	}
       public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
		this.beanFactory = beanFactory;
		createAdvisorChain();

		if (this.singleton) {
			this.targetSource = freshTargetSource();
			if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
				// Rely on AOP infrastructure to tell us what interfaces to proxy.
				setInterfaces(ClassUtils.getAllInterfacesForClass(this.targetSource.getTargetClass()));
			}
			// Eagerly initialize the shared singleton instance.
			getSingletonInstance();
			// We must listen to superclass advice change events to recache the singleton
			// instance if necessary.
			addListener(this);
		}
	}

/**
	 * Return a proxy. Invoked when clients obtain beans from this factory bean.
	 * Create an instance of the AOP proxy to be returned by this factory.
	 * The instance will be cached for a singleton, and create on each call to
	 * <code>getObject()</code> for a proxy.
	 * @return a fresh AOP proxy reflecting the current state of this factory
	 */
	public Object getObject() throws BeansException {
		if (isSingleton()) {
			return getSingletonInstance();
		}
		else {
			if (this.targetName == null) {
				logger.warn("Using non-singleton proxies with singleton targets is often undesirable." +
						"Enable prototype proxies by setting the 'targetName' property.");
			}
			return newPrototypeInstance();
		}
	}
/**
	 * Create a new prototype instance of this class's created proxy object,
	 * backed by an independent AdvisedSupport configuration.
	 * @return a totally independent proxy, whose advice we may manipulate in isolation
	 */
	private synchronized Object newPrototypeInstance() {
		// In the case of a prototype, we need to give the proxy
		// an independent instance of the configuration.
		// In this case, no proxy will have an instance of this object's configuration,
		// but will have an independent copy.
		if (logger.isDebugEnabled()) {
			logger.debug("Creating copy of prototype ProxyFactoryBean config: " + this);
		}

		AdvisedSupport copy = new AdvisedSupport();
		// The copy needs a fresh advisor chain, and a fresh TargetSource.
		TargetSource targetSource = freshTargetSource();
		copy.copyConfigurationFrom(this, targetSource, freshAdvisorChain());
		if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
			// Rely on AOP infrastructure to tell us what interfaces to proxy.
			copy.setInterfaces(ClassUtils.getAllInterfacesForClass(targetSource.getTargetClass()));
		}
		copy.setFrozen(this.freezeProxy);

		if (logger.isDebugEnabled()) {
			logger.debug("Copy has config: " + copy);
		}
		return getProxy(copy.createAopProxy());
	}
/**
	 * Return the proxy object to expose.
	 * <p>The default implementation uses a <code>getProxy()</code> call.
	 * Can be overridden to specify a custom class loader.
	 * @param aopProxy the prepared AopProxy instance to get the proxy from
	 * @return the proxy object to expose
	 * @see AopProxy#getProxy()
	 * @see AopProxy#getProxy(ClassLoader)
	 */
	protected Object getProxy(AopProxy aopProxy) {
		return aopProxy.getProxy();
	}
}

public class AdvisedSupport extends ProxyConfig implements Advised {
protected synchronized AopProxy createAopProxy() {
		if (!this.active) {
			activate();
		}
		return getAopProxyFactory().createAopProxy(this);
	}
}

public class DefaultAopProxyFactory implements AopProxyFactory {
public AopProxy createAopProxy(AdvisedSupport advisedSupport) throws AopConfigException {
		if (advisedSupport.isOptimize() || advisedSupport.isProxyTargetClass() ||
		    advisedSupport.getProxiedInterfaces().length == 0) {
			if (!cglibAvailable) {
				throw new AopConfigException(
						"Cannot proxy target class because CGLIB2 is not available. " +
						"Add CGLIB to the class path or specify proxy interfaces.");
			}
			return CglibProxyFactory.createCglibProxy(advisedSupport);
		}
		else {
			return new JdkDynamicAopProxy(advisedSupport);
		}
	}
}


JdkDynamicAopProxy 源码,可见底层实现中是借住了JDK中实现动态代理模式的方法:implements InvocationHandler, 核心上来说就是实现invoke()方法。


final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {

/**
	 * Construct a new JDK proxy.
	 * @throws AopConfigException if the config is invalid. We try
	 * to throw an informative exception in this case, rather than let
	 * a mysterious failure happen later.
	 */
	protected JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {
		if (config == null) {
			throw new AopConfigException("Cannot create AopProxy with null ProxyConfig");
		}
		if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {
			throw new AopConfigException("Cannot create AopProxy with no advisors and no target source");
		}
		this.advised = config;
	}


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

	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);
		return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
	}



	/**
	 * Implementation of <code>InvocationHandler.invoke</code>.
	 * <p>Callers will see exactly the exception thrown by the target,
	 * unless a hook method throws an exception.
	 */
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		MethodInvocation invocation = null;
		Object oldProxy = null;
		boolean setProxyContext = false;

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

		try {
			if (AopUtils.isEqualsMethod(method)) {
				// This class implements the equals(Object) method itself.
				return equals(args[0]) ? Boolean.TRUE : Boolean.FALSE;
			}
			if (AopUtils.isHashCodeMethod(method)) {
				// This class implements the hashCode() method itself.
				return new Integer(hashCode());
			}
			if (Advised.class == method.getDeclaringClass()) {
				// Service invocations on ProxyConfig with the proxy config...
				return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
			}

			Object retVal = null;

			if (this.advised.exposeProxy) {
				// Make invocation available if necessary.
				oldProxy = AopContext.setCurrentProxy(proxy);
				setProxyContext = true;
			}

			// May be <code>null</code>. Get as late as possible to minimize the time we "own" the target,
			// in case it comes from a pool.
			target = targetSource.getTarget();
			if (target != null) {
				targetClass = target.getClass();
			}

			// Get the interception chain for this method.
			List chain = this.advised.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
					this.advised, proxy, 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.
				retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);
			}
			else {
				// 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();
			}

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


JDK动态代理为什么必须用接口以及与CGLIB的对比 

这两天对AOP原理感兴趣了,试验了JDK动态代理与CGLIB动态代理。从SpringAOP框架介绍中得知对于使用接口的类,Spring使用JDK动态代理(原来做项目中试图从Bean强制转换为实现类,结果报错,原来是这么回事),没有接口的就使用别的AOP框架aspectj,但这些都是依赖于Java字节码工具ASM生成一个原类的新类,调用Callback

但是JDK动态代理为什么必须使用接口一直很疑惑,难道原理不是像ASM一样修改字节码吗?带着这个疑问,开始看JDKProxy代码。使用JDK动态代理的代码代码

ITestBean tb = (ITestBean)Proxy.newProxyInstance(tb.getClass().getClassLoader(),tb.getClass().getInterfaces(), new TestBeanHander(tb));

于是从创建代理函数看起,即

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException  

通过源码可以看到,这个类第一步生成一个代理类(注意,这里的参数就是接口列表),

Classcl = getProxyClass(loader, interfaces);

然后通过代理类找到构造参数为InvocationHandler的构造函数并生成一个新类。

Constructor cons =cl.getConstructor(constructorParams);//这个有用,在后面细说
return (Object) cons.newInstance(new Object[] { h });  

接口起什么作用呢,于是又看getProxyClass方法的代码,这个源码很长,就不细说了。大致分为三段:

第一:验证

第二:缓存创建新类的结构,如果创建过,则直接返回。(注意:这里的KEY就是接口列表)

第三:如果没有创建过,则创建新类

创建代码如下

    long num;
   //
获得代理类数字标识 

   synchronized (nextUniqueNumberLock){
     num = nextUniqueNumber++;
    }

    //获得创建新类的类名$Proxy,包名为接口包名,但需要注意的是,如果有两个接口而且不在同一个包下,也会报错

    String proxyName = proxyPkg + proxyClassNamePrefix + num;
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces);  //调用class处理文件生成类的字节码,根据接口列表创建一个新类-代理类

    proxyClass= defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);  //通过JNI(Java Native Interface)接口,将Class字节码文件定义一个新类

根据前面的代码Constructor cons = cl.getConstructor(constructorParams);

可以猜测到接口创建的新类proxyClassFile 不管采用什么接口,都是以下结构

    public class $Proxy1 extends Proxy implements传入的接口{}

生成新类的看不到源代码,不过猜测它的执行原理很有可能是如果类是Proxy的子类,则调用InvocationHandler进行方法的Invoke

到现在大家都应该明白了吧,JDK动态代理的原理是根据定义好的规则,用传入的接口创建一个新类,这就是为什么采用动态代理时为什么只能用接口引用指向代理,而不能用传入的类引用执行动态类, 创建的新类是继承了Proxy类了,所以只能实现原来对象的接口。

CGLIB 采用的是用创建一个继承实现类的子类,用asm库动态修改子类的代码来实现的,所以可以用传入的类引用执行代理类。由此可以简单总结:JDK动态代理在实现的时候继承了Proxy类然后实现了被代理类的接口,而CGLIB动态代理是继承了被代理类,然后修改了子类源码。

JDK动态代理与CGLIB对比如下:

//JDK动态代理测试代码

ITestBean tb = new TestBean();
tb = (ITestBean) Proxy.newProxyInstance(tb.getClass().getClassLoader(),tb.getClass().getInterfaces(), new TestBeanHander(tb));//
这句用接口引用指向,不会报错

TestBeantmp = (TestBean) tb;//强制转换为实现类,将抛出类强制转换异常

//CGLIB测试代码

TestProxytp = new TestProxy(); tb = (ITestBean) tp.getProxy(TestBean.class);

tmp= (TeatBean) tb;//强制转换为实现类,不会抛出异常

补充说明,如果在实现类中,接口定义的方法互相调用不会在调用InvocationHandlerinvoke方法,JDK动态代理应该不是嵌入到Java的反射机制中,而是在反射机制上的一个调用。 

一个简单的使用接口动态代理的例子,即实现InvocationHandler


import java.lang.reflect.InvocationHandler;
 4import java.lang.reflect.Method;
 5import java.lang.reflect.Proxy;
 6
 7public class DynaProxyHello implements InvocationHandler {
 8    /**
 9     * 操作者
10     */
11    private Object proxy;
12    /**
13     * 要处理的对象(也就是我们要在方法的前后加上业务逻辑的对象,如例子中的Hello)
14     */
15    private Object delegate;
16
17    /**
18     * 动态生成方法被处理过后的对象 (写法固定)
19     * 
20     * @param delegate
21     * @param proxy
22     * @return
23     */
24    public Object bind(Object delegate,Object proxy) {
25        
26        this.proxy = proxy;
27        this.delegate = delegate;
28        return Proxy.newProxyInstance(
29                this.delegate.getClass().getClassLoader(), this.delegate
30                        .getClass().getInterfaces(), this);
31    }
32    /**
33     * 要处理的对象中的每个方法会被此方法送去JVM调用,也就是说,要处理的对象的方法只能通过此方法调用
34     * 此方法是动态的,不是手动调用的
35     */
36    public Object invoke(Object proxy, Method method, Object[] args)
37            throws Throwable {
38        Object result = null;
39        try {
40            //反射得到操作者的实例
41            Class clazz = this.proxy.getClass();
42            //反射得到操作者的Start方法
43            Method start = clazz.getDeclaredMethod("start",
44                    new Class[] { Method.class });
45            //反射执行start方法
46            start.invoke(this.proxy, new Object[] { method });
47            //执行要处理对象的原本方法
48            result = method.invoke(this.delegate, args);
49//            反射得到操作者的end方法
50            Method end = clazz.getDeclaredMethod("end",
51                    new Class[] { Method.class });
52//            反射执行end方法
53            end.invoke(this.proxy, new Object[] { method });
54
55        } catch (Exception e) {
56            e.printStackTrace();
57        }
58        return result;
59    }
60
61}

 



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值