Java动态代理详解

        代理模式使我们可以做到很多不同的事情,比如封装真实对象,使其对客户端透明(安全性);跨域访问(远程代理)等等。代理可以被分为静态代理和动态代理,我们可以使用动态代理来延迟创建我们所需的对象(很多程序往往并不是一开始就需要所有的对象,特别是对于某些耗费很多资源的对象)。

        在谈论动态代理之前,我们先来回顾一下代理模式设计到的角色及其相互关系。

        java中的代理涉及到四个不同的角色:代理的协议接口Subject(暴露给Client),协议接口的具体实现 RealSubject , 代理对象SubjectProxy,Client。相互关系如图1所示。



图1 代理模式UML图

        在静态代理中,SubjectProxy一般是由我们人工创建的,但是使用java的动态代理,我们可以让机器帮我们创建这个对象。那么java中究竟是如何做到这一点的呢?

        java动态代理除了上述代理模式中的Client、Subject、RealSubject之外,还涉及到另外两个对象:InvocationHandler 和 Proxy (均位于java反射包中),其中Proxy负责创建代理对象,即上文提到的SubjectProxy,所有对SubjectProxy 方法的访问都会映射到InvocationHandler 实现类的invoke方法上面。

        我们先来看一个动态代理的例子:

ProxyTestSuite.java

import java.io.PrintWriter;

 /**
  * 
  * @author H.John
  */
public class ProxyTestSuite {
	public static void main(String[] args) {
		Subject subject =  (Subject) ProxyHandler.createProxy(
				new RealSubject(),new PrintWriter(System.out,true));
		subject.request();
	}
}
/**
 * 
 * 代理协议接口
 */
interface Subject{
	void request();
}

/**
 * 
 * 真实对象
 *
 */
class RealSubject implements Subject{
	
	public void request(){
		System.out.println("i am a real subject");
	}
}

 ProxyHandler.java

/**
 * 
 * @author H.John
 * 代理实例调用处理程序。
 * 对代理实例调用方法时,将对方法调用进行编码并将其指派到它的调用处理程序的 invoke方法。 
 */
public class ProxyHandler implements InvocationHandler {

	//真实被代理对象引用
	private Object target_;
	private PrintWriter writer_;
	
	public ProxyHandler(Object target, PrintWriter writer) {
		target_ = target;
		writer_ = writer;
	}

	public static Subject createProxy(Object target,PrintWriter writer) {
		return (Subject) Proxy.newProxyInstance(target.getClass().getClassLoader(), 
				target.getClass().getInterfaces(), 
				new ProxyHandler(target, writer));

	}
	
	/**
	 * 对代理实例调用方法时触发的方法,通过此方法,以反射的方式去调用代理实例的实现方法。
	 */
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

		Object result;
		try {
			writer_.println(method.getName());
			result = method.invoke(target_, args);
			
		} catch (Exception e) {
			writer_.println(method.getName() + " throws " + e.getCause());  
            throw e.getCause();  
		}
		writer_.println(method.getName() + " returns");  
        return result;
	}		
}
        那么上述几个角色在java动态代理中是怎样被联系起来的呢?下面我们就通过阅读源码来找寻我们需要的答案。

       一切的奥秘均在Proxy类中:

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        if (h == null) {
            throw new NullPointerException();
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class cl = getProxyClass(loader, interfaces);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            Constructor cons = cl.getConstructor(constructorParams);
            return (Object) cons.newInstance(new Object[] { h });
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        } catch (IllegalAccessException e) {
            throw new InternalError(e.toString());
        } catch (InstantiationException e) {
            throw new InternalError(e.toString());
        } catch (InvocationTargetException e) {
            throw new InternalError(e.toString());
        }
    }

         我们可以用上述方法来创建所需的代理对象,传入三个参数,类加载器,被代理对象subject实现的接口,handler实现类(使得代理对象和被代理对象联系起来的类,该类持有被代理对象RealSubject)。

        在newProxyInstance方法中,有2个关键步骤:

        (1) 获取getProxyClass方法获取代理类的Class。

        (2) 通过Class获取参数为InvocationHandler的构造方法,并创建代理对象。

       那么我们想知道getProxyClass方法中究竟干了什么事情?

{
                /*
                 * Choose a name for the proxy class to generate.
                 */
                long num;
                synchronized (nextUniqueNumberLock) {
                    num = nextUniqueNumber++;
                }
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
                /*
                 * Verify that the class loader hasn't already
                 * defined a class with the chosen name.
                 */

                /*
                 * Generate the specified proxy class.
                 */
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces);
                try {
                    proxyClass = defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {
                    /*
                     * A ClassFormatError here means that (barring bugs in the
                     * proxy class generation code) there was some other
                     * invalid aspect of the arguments supplied to the proxy
                     * class creation (such as virtual machine limitations
                     * exceeded).
                     */
                    throw new IllegalArgumentException(e.toString());
                }
}

         我们可以看到在getProxyClass方法中,程序通过ProxyGenerator.generateProxyClass(...)方法帮我们做了我们平时都在做的编码工作,只不过是没有生成源代码,直接根据class文件的格式生成了一个具有InvocationHandler实例对象的class二进制文件。再通过native方法defineClass0去构造我们平时都在用的Class对象。

ok,到现在为止,我们对java动态反射的运用以及jdk的内部实现都有了一个比较初步的了解。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值