最新消息:

java代理模式(proxy)及动态代理的源码分析

JAVA 大步 714浏览 0评论
• 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。
• 在某些情况下,一个客户不想或者不能直引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用

代理模式的角色

– 抽象角色:声明真实对象和代理对象的共同接口
– 代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装
– 真实角色:代理角色所代表的真实对象,是我们最终要引用的对象
 
 
 
实例:
 

共同的接口:Subject.java:
真是的角色:RealSubject.java:
代理角色:ProxySubject.java


}

客户端:


}

 
 
• 由以上代码可以看出,客户实际需要调用的是RealSubject类的request()方法,现在用ProxySubject来代理 RealSubject类,同样达到目的,同时还封装了其他方法(preRequest(),postRequest()),可以处理一些其他问题。
• 另外,如果要按照上述的方法使用代理模式,那么真实角色必须是事先已经存在的,并将其作为代理对象的内部属。但是实际使用时,一个真实角色必须对应一个 代理
角色,如果大量使用会导致类的急剧膨胀;此外,如果事先并不知道真实角色,该如何使用代理呢?这个问题可以过Java的动态代理类来解决
 
 
 

动态代理类

 
 Java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
 (1)Interface InvocationHandler:该接口中仅定义了一个方法
在实际使用时,第一个参数obj一般是指代理类method是被代理的方法,如上例中的request(),args为该方法的参数数组。 这个抽象方法在代理类中动态实
 
public interface InvocationHandler
InvocationHandler is the interface implemented by the invocation handler of a proxy instance.
Each proxy instance has an associated invocation handler. When a method is invoked on a proxy instance, the method invocation is encoded and dispatched to the invoke method of its invocation handler.(每一个代理实例都会有个与之关联的调用处理器。当一个方法被一个代理实例调用时,这个方法的调用被编码并分发到它的调用处理器的invoke 方法中。
 

 

 
 
 (2)Proxy:该类即为动态代理类,作用类似于上例中的ProxySubject,其中主要包含以下内容
• protected Proxy(InvocationHandler h):构造函数,用于给内部的h赋值。
static Class getProxyClass (ClassLoader loader, Class[] interfaces):获得一个代理类,其中loader是类装载器,interfaces是真实类所拥有的全部接口的数组
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h):返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在Subject接口中声明过的方法)
 
 
 
所谓Dynamic Proxy是这样一种class:它是在运行时生成的class,在生成它时你必须提供一组interface给它,然后该class就宣称它实现了这些 interface。你当然可以把该class的实例当作这些interface中的任何一个来用。当然,这个Dynamic Proxy其实就是一个Proxy,它不会替你作实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作
 
 
• 在使用动态代理类时,我们必须实现InvocationHandler接口
 
实例:动态代理
共同的接口:Subject.java:
真是的角色:RealSubject.java:
动态代理类


}

客户端:

}

打印:
注意:Proxy.newProxyInstance()方法生成的对象,既不是Subject类,也不是DynamicSubject类,而是 com.sun.proxy.$Proxy0 ,而这个类实现了Subject接口,所以才能强制转换

java动态代理源码分析

上面的实例中,我们发现通过Proxy.newProxyInstance()方法生成的实例对象既不是Subject类,也不是DynamicSubject类,而是 com.sun.proxy.$Proxy0 ,而这个类实现了Subject接口,所以才能强制转换。那么,这个类是怎么生成的呢? 只能看下jdk的源码了。
先看Proxy.newProxyInstance()的源码:
看到其中   Class<?> cl = getProxyClass0(loader, intfs);  ,去查看 getProxyClass0(loader, intfs); 源码:
接着去看proxyClassCache.get(loader, interfaces);  ,先看看proxyClassCache是什么东西?
 
源码注释说的很明白了,是代理的缓存。我们看到WeakCache的初始化中,有new ProxyClassFactory(),这个类就是真正生成的代理类,是Proxy类的一个静态内部类,继续跟踪进去:
通过这个类的代码,我们知道了为什么生成的类的名字叫com.sun.proxy.$Proxy0 了。这个静态内部类只有一个apply方法,这个apply方法内部调用 sun.misc.ProxyGenerator 来生成这个代理类的字节码。我们的疑惑大部分揭晓了。那么这个apply方法在什么地方会被调用呢?
见   proxyClassCache.get(loader, interfaces);    ,即  WeakCache的get方法会调用ProxyClassFactory实例对象的apply方法,以此动态生成代理类,并返回。
我们看下ProxyGenerator.generateProxyClass()方法:

根据注释,我们知道,只要saveGeneratedFiles这个值为true,就可以将生成的class文件保存到项目根目录下的 com/sun/proxy/ 目录中,前提是要先创建 com/sun/proxy/ 目录,否则会提示"I/O exception saving generated file: "错误。
 
那么如何设置saveGeneratedFiles为true呢?
由于saveGeneratedFiles = ((Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();
而new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles") 最后的值是来自于它的
再看下Boolean.getBoolean(this.theProp):
可以发现最后是获取系统的这个"sun.misc.ProxyGenerator.saveGeneratedFiles"的值,所以,我们可以手动设置这个值为true,如下:
最后贴上修改后的Client.java代码:

}

运行后,会在项目根目录下的 com/sun/proxy/$Proxy0.class文件,目录结构:

反编译下$Proxy0.class文件,看下内容:
1.可以看到代理类继承自Proxy,而实现了我们定义的接口。如果我们定义了多个接口,则该动态类也会实现多个接口。
2.生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。
3.重写了euqals,hashCode,toString方法,它们都只是简单的调用了InvocationHandler的invoke方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法。
4.实现了Subject接口的request()方法,该方法也只是InvocationHandler的invoke方法。而我们通过这个源码,知道了InvocationHandler的invoke(Object proxy, Method method, Object[] args) 中method参数就是request方法,而因为request()方法是无参方法,所以args参数为null。
 
 
下面是使用动态代理,生成Vector的实例:


}

 
• 通过这种方式,被代理的对象(RealSubject)可以在运行时动态改变,需要控制的接口(Subject接口)可以在运行时改变,控制的方式(DynamicSubject类)也可以动态改变,从而实现了非常灵活的动态代理关系
 
 
 
 
 动态代理是指客户通过代理类来调用其它对象的方法
动态代理使用场合:
• 调试
• 远程方法调用(RMI)

动态代理的步骤:

1.创建一个实现接口InvocationHandler的类,它必须实现invoke方法
2.创建被代理的类以及接口
3.通过Proxy的静态方法newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 创建一个代理
4.通过代理调用方法
 
 

 
 
 
 
 
 
来自为知笔记(Wiz)

转载请注明:大步's Blog » java代理模式(proxy)及动态代理的源码分析

SiteMap