您的位置:首页 > 编程语言 > Java开发

JAVA动态代理内部实现

2010-05-09 13:25 555 查看

JAVA动态代理内部实现

一 代理设计模式

代理模式为目标对象提供一种代理以控制对实际对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。

为了保持行为的一致性,代理类和实际委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。



 代理模式类图  

常见的代理有:
1) 远程代理(Remote proxy):对一个位于不同的地址空间对象提供一个局域代表对象,如RMI中的stub。 
2) 虚拟代理(Virtual proxy):根据需要将一个资源消耗很大或者比较复杂的对象,延迟加载,在真正需要的时候才创建。
3) 保护代理(Protect or Access Proxy):控制对一个对象的访问权限。
4) 智能引用(Smart Reference Proxy):提供比目标对象额外的服务和功能。

通过代理类这一中间层,能够有效控制对实际委托类对象的直接访问,也可以很好地隐藏和保护实际对,实施不同的控制策略,从而在设计上获得了更大的灵活性。

二 动态代理使用

JAVA动态代理机制以巧妙的方式实现了代理模式的设计理念。



动态代理类图

动态代理在代理ProxySubject和RealSubject之间增加了InvocationHandler,这是一种通信间接化, 增加了灵 性性,例如可以把这个中间层实现为一个框架Framework,直接通过xml文件等方式来调用RealSubject。

在普通的设计中,我们一般不会使用动态代理。但是在一些框架结构设计中,动态代理非常重要,如RMI,EJB中都使用动态代理。

view plaincopy to clipboardprint?

interface Subject

{

public void doSomething();

}

class RealSubject implements Subject

{

public void doSomething()

{

System.out.println( "call doSomething()" );

}

}

class ProxyHandler implements InvocationHandler

{

private Object proxied;

public ProxyHandler( Object proxied )

{

this.proxied = proxied;

}

public Object invoke( Object proxy, Method method, Object[] args ) throws Throwable

{

return method.invoke( proxied, args);

}

}

view plaincopy to clipboardprint?

import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;

import java.lang.reflect.Proxy;

import sun.misc.ProxyGenerator;

import java.io.*;

public class DynamicProxy

{

public static void main( String args[] )

{

RealSubject real = new RealSubject();

Subject proxySubject = ( Subject )   

           Proxy.newProxyInstance(

           Subject.class.getClassLoader(),

   new Class[] { Subject.class },

 new ProxyHandler( real ) );

proxySubject.doSomething();

  //write proxySubject class binary data to file

createProxyClassFile();

}

public static void createProxyClassFile()

{

String name = "ProxySubject";

byte[] data = ProxyGenerator.generateProxyClass( name, new Class[] { Subject.class } );

try

{

FileOutputStream out = new FileOutputStream( name + ".class" );

out.write( data );

out.close();

}

catch( Exception e )

{

e.printStackTrace();

}

}

}

三 动态代理内部实现

类Proxy的getProxyClass方法调用ProxyGenerator的 generateProxyClass方法产生ProxySubject.class的二进制数据:

public static byte[] generateProxyClass(final String name, Class[] interfaces)

我们可以import sun.misc.ProxyGenerator,调用 generateProxyClass方法产生binary data,然后写入文件,最后通过反编译工具来查看内部实现原理。

反编译后的ProxySubject.java:

view plaincopy to clipboardprint?

import java.lang.reflect.*;

public final class ProxySubject extends Proxy

implements Subject

{

private static Method m1;

private static Method m0;

private static Method m3;

private static Method m2;

public ProxySubject(InvocationHandler invocationhandler)

{

super(invocationhandler);

}

public final boolean equals(Object obj)

{

try

{

return ((Boolean)super.h.invoke(this, m1, new Object[] {

obj

})).booleanValue();

}

catch(Error _ex) { }

catch(Throwable throwable)

{

throw new UndeclaredThrowableException(throwable);

}

}

public final int hashCode()

{

try

{

return ((Integer)super.h.invoke(this, m0, null)).intValue();

}

catch(Error _ex) { }

catch(Throwable throwable)

{

throw new UndeclaredThrowableException(throwable);

}

}

public final void doSomething()

{

try

{

super.h.invoke(this, m3, null);

return;

}

catch(Error _ex) { }

catch(Throwable throwable)

{

throw new UndeclaredThrowableException(throwable);

}

}

public final String toString()

{

try

{

return (String)super.h.invoke(this, m2, null);

}

catch(Error _ex) { }

catch(Throwable throwable)

{

throw new UndeclaredThrowableException(throwable);

}

}

static

{

try

{

m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {

Class.forName("java.lang.Object")

});

m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);

m3 = Class.forName("Subject").getMethod("doSomething", new Class[0]);

m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);

}

catch(NoSuchMethodException nosuchmethodexception)

{

throw new NoSuchMethodError(nosuchmethodexception.getMessage());

}

catch(ClassNotFoundException classnotfoundexception)

{

throw new NoClassDefFoundError(classnotfoundexception.getMessage());

}

}

}

通过 ProxySubject.java,我们可以看到动态代理的内部是如何实现的,并且我们可以实现自己的一个动态代理生成器。

ProxyGenerator内部是如何生成class二进制数据,可以参考源代码。

view plaincopy to clipboardprint?

private byte[] generateClassFile() {

/*

* Record that proxy methods are needed for the hashCode, equals,

* and toString methods of java.lang.Object. This is done before

* the methods from the proxy interfaces so that the methods from

* java.lang.Object take precedence over duplicate methods in the

* proxy interfaces.

*/

addProxyMethod(hashCodeMethod, Object.class);

addProxyMethod(equalsMethod, Object.class);

addProxyMethod(toStringMethod, Object.class);

/*

* Now record all of the methods from the proxy interfaces, giving

* earlier interfaces precedence over later ones with duplicate

* methods.

*/

for (int i = 0; i < interfaces.length; i++) {

Method[] methods = interfaces[i].getMethods();

for (int j = 0; j < methods.length; j++) {

addProxyMethod(methods[j], interfaces[i]);

}

}

/*

* For each set of proxy methods with the same signature,

* verify that the methods' return types are compatible.

*/

for (List<ProxyMethod> sigmethods : proxyMethods.values()) {

checkReturnTypes(sigmethods);

}

/* ============================================================

* Step 2: Assemble FieldInfo and MethodInfo structs for all of

* fields and methods in the class we are generating.

*/

try {

methods.add(generateConstructor());

for (List<ProxyMethod> sigmethods : proxyMethods.values()) {

for (ProxyMethod pm : sigmethods) {

// add static field for method's Method object

fields.add(new FieldInfo(pm.methodFieldName,

"Ljava/lang/reflect/Method;",

ACC_PRIVATE | ACC_STATIC));

// generate code for proxy method and add it

methods.add(pm.generateMethod());

}

}

methods.add(generateStaticInitializer());

} catch (IOException e) {

throw new InternalError("unexpected I/O Exception");

}

/* ============================================================

* Step 3: Write the final class file.

*/

/*

* Make sure that constant pool indexes are reserved for the

* following items before starting to write the final class file.

*/

cp.getClass(dotToSlash(className));

cp.getClass(superclassName);

for (int i = 0; i < interfaces.length; i++) {

cp.getClass(dotToSlash(interfaces[i].getName()));

}

/*

* Disallow new constant pool additions beyond this point, since

* we are about to write the final constant pool table.

*/

cp.setReadOnly();

ByteArrayOutputStream bout = new ByteArrayOutputStream();

DataOutputStream dout = new DataOutputStream(bout);

try {

/*

* Write all the items of the "ClassFile" structure.

* See JVMS section 4.1.

*/

// u4 magic;

dout.writeInt(0xCAFEBABE);

// u2 minor_version;

dout.writeShort(CLASSFILE_MINOR_VERSION);

// u2 major_version;

dout.writeShort(CLASSFILE_MAJOR_VERSION);

cp.write(dout); // (write constant pool)

// u2 access_flags;

dout.writeShort(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);

// u2 this_class;

dout.writeShort(cp.getClass(dotToSlash(className)));

// u2 super_class;

dout.writeShort(cp.getClass(superclassName));

// u2 interfaces_count;

dout.writeShort(interfaces.length);

// u2 interfaces[interfaces_count];

for (int i = 0; i < interfaces.length; i++) {

dout.writeShort(cp.getClass(

dotToSlash(interfaces[i].getName())));

}

// u2 fields_count;

dout.writeShort(fields.size());

// field_info fields[fields_count];

for (FieldInfo f : fields) {

f.write(dout);

}

// u2 methods_count;

dout.writeShort(methods.size());

// method_info methods[methods_count];

for (MethodInfo m : methods) {

m.write(dout);

}

// u2 attributes_count;

dout.writeShort(0); // (no ClassFile attributes for proxy classes)

} catch (IOException e) {

throw new InternalError("unexpected I/O Exception");

}

return bout.toByteArray();
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: