加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Java 动态代理机制分析及扩展--转

发布时间:2020-12-14 06:17:28 所属栏目:Java 来源:网络整理
导读:h2 id="major2" style="margin: 5px 0px; padding: 0px; border: 0px; outline: 0px; font-size: 1.6em !important; vertical-align: baseline; font-family: HelveticaNeue-Light,'Helvetica Neue Light','Helvetica Neue',Helvetica,Arial; color: #000000

<h2 id="major2" style="margin: 5px 0px; padding: 0px; border: 0px; outline: 0px; font-size: 1.6em !important; vertical-align: baseline; font-family: HelveticaNeue-Light,'Helvetica Neue Light','Helvetica Neue',Helvetica,Arial; color: #000000; font-style: normal; font-variant: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff;">引言
<p style="margin-top: 5px !important; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding: 6px 0px; border: 0px; outline: 0px; font-size: 1.166em !important; vertical-align: baseline; font-family: Arial,sans-serif; color: #222222; line-height: 1.5em; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff;">Java 动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架。通过阅读本文,读者将会对 Java 动态代理机制有更加深入的理解。本文首先从 Java 动态代理的运行机制和特点出发,对其代码进行了分析,推演了动态生成类的内部实现。


<p class="ibm-ind-link ibm-back-to-top" style="margin: 0px; padding: 6px 0px; border: 0px; outline: 0px; font-size: 1.166em !important; vertical-align: baseline; font-family: Arial,sans-serif; color: #222222; clear: both; text-align: right; height: 22px; line-height: 1.5em; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff;"><a class="ibm-anchor-up-link" style="margin: 0px; padding: 0px 0px 0px 16px; border-width: 0px; border-bottom-style: none; outline: 0px; font-size: inherit; vertical-align: baseline; color: #745285; font-family: Arial,sans-serif; opacity: 0.8; background-image: url('http://1.www.s81c.com/i/v17/icons/ibm_sprite_arrow_blue_ON.png') !important; display: inline; text-decoration: none; line-height: 1.065em; font-weight: bold; background-position: 0px -401px; background-repeat: no-repeat no-repeat;" href="http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/#ibm-pcon"&gt;回页首


<h2 id="major3" style="margin: 5px 0px; padding: 0px; border: 0px; outline: 0px; font-size: 1.6em !important; vertical-align: baseline; font-family: HelveticaNeue-Light,Arial; color: #000000; font-style: normal; font-variant: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff;">代理:设计模式
<p style="margin-top: 5px !important; margin-right: 0px; margin-bottom: 0px; margin-left: 0px; padding: 6px 0px; border: 0px; outline: 0px; font-size: 1.166em !important; vertical-align: baseline; font-family: Arial,sans-serif; color: #222222; line-height: 1.5em; font-style: normal; font-variant: normal; font-weight: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff;">代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。


<h5 id="fig1" style="margin: 5px 0px 0px; padding: 0px; border: 0px; outline: 0px; font-size: 1.166em !important; vertical-align: baseline; color: #000000; font-family: Arial,sans-serif; font-style: normal; font-variant: normal; letter-spacing: normal; orphans: auto; text-align: start; text-indent: 0px; text-transform: none; white-space: normal; widows: auto; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: #ffffff;">图 1. 代理模式

图 1. 代理模式

    // 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象
    static Class getProxyClass(ClassLoader loader,Class[] interfaces)

    // 方法 3:该方法用于判断指定类对象是否是一个动态代理类
    static boolean isProxyClass(Class cl)

    // 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
    static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h)

// 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象
Class clazz = Proxy.getProxyClass(classLoader,new Class[] { Interface.class,... });

// 通过反射从生成的类对象获得构造函数对象
Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class });

// 通过构造函数对象创建动态代理类实例
Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });

// 通过 Proxy 直接创建动态代理类实例
Interface proxy = (Interface)Proxy.newProxyInstance( classLoader,new Class[] { Interface.class },handler );

图 2. 动态代理类的继承图

// 标记:用于标记一个动态代理类正在被创建中
private static Object pendingGenerationMarker = new Object();

// 同步表:记录已经被创建的动态代理类类型,主要被方法 isProxyClass 进行相关的判断
private static Map proxyClasses = Collections.synchronizedMap(new WeakHashMap());

// 关联的调用处理器引用
protected InvocationHandler h;

// 由于 Proxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用
protected Proxy(InvocationHandler h) {this.h = h;}

[] interfaces,InvocationHandler h) throws IllegalArgumentException {
// 检查 h 不为空,否则抛异常
if (h == null) { 
    throw new NullPointerException(); 
} 

// 获得与制定类装载器和一组接口相关的代理类类型对象
Class cl = getProxyClass(loader,interfaces); 

// 通过反射获取构造函数对象并生成代理类实例
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()); 
} 

}

    。总体上这部分实现比较直观,所以略去大部分代码,仅保留留如何判断某类或接口是否对特定类装载器可见的相关代码。
  1. // 把生成的代理类的类对象记录进 proxyClasses 表
    proxyClasses.put(proxyClass,null);

// 假设代理类为 SimulatorProxy,其类声明将如下
final public class SimulatorProxy implements Simulator {

// 调用处理器对象的引用
protected InvocationHandler handler; 

// 以调用处理器为参数的构造函数
public SimulatorProxy(InvocationHandler handler){ 
    this.handler = handler; 
} 

// 实现接口方法 simulate 
public short simulate(int arg1,String arg3) 
    throws ExceptionA,ExceptionB {

    // 第一步是获取 simulate 方法的 Method 对象
    java.lang.reflect.Method method = null; 
    try{ 
        method = Simulator.class.getMethod( 
            "simulate",new Class[] {int.class,long.class,String.class} );
    } catch(Exception e) { 
        // 异常处理 1(略)
    } 

    // 第二步是调用 handler 的 invoke 方法分派转发方法调用
    Object r = null; 
    try { 
        r = handler.invoke(this,method,// 对于原始类型参数需要进行装箱操作
            new Object[] {new Integer(arg1),new Long(arg2),arg3});
    }catch(Throwable e) { 
        // 异常处理 2(略)
    } 
    // 第三步是返回结果(返回类型是原始类型则需要进行拆箱操作)
    return ((Short)r).shortValue();
} 

}

try {
r = handler.invoke(this,new Object[] {new Integer(arg1),arg3});

} catch( ExceptionA e) {

// 接口方法支持 ExceptionA,可以抛出
throw e; 

} catch( ExceptionB e ) {
// 接口方法支持 ExceptionB,可以抛出
throw e;

} catch(Throwable e) {
// 其他不支持的异常,一律抛 UndeclaredThrowableException
throw new UndeclaredThrowableException(e);
}

图 1. ProxyEx 类继承图

// 方法声明以及实现
String body = bodyTemplate.replaceAll("&Declaration",declare )
.replaceAll("&Body",getMethodEntity( method ));

String result = template.replaceAll("&amp;MethodName",method.getName() ) .replaceAll("&amp;Class",method.getDeclaringClass().getName() + ".class") .replaceAll("&amp;ParameterTypes",getMethodParameterTypesHelper(method)) .replaceAll("&amp;ParameterValues",getMethodParameterValuesHelper(method) ) .replaceAll("&amp;Exceptions",getMethodParameterThrowablesHelper(method)) .replaceAll("&amp;Return",getMethodReturnHelper( method ) ); return result;

}

&Return

// 通过扩展调用处理器获得stub对象 Object stub = ((InvocationHandlerEx)handler).getStub(stubClass); if( stub != null ) { // 获得所有需同步的类成员列表,遍历并同步 java.lang.reflect.Field[] fields = getFields(superClass); for(int i=0; fields!=null&amp;&amp;i<fields.length; i++) { try { fields[i].setAccessible(true); // 执行代理类和被代理类的变量同步 if(toStub) { fields[i].set(stub,fields[i].get(this)); } else { fields[i].set(this,fields[i].get(stub)); } } catch(Throwable e) { } } } }

}

java.lang.reflect.Field[] fields = null; if( c == java.lang.Object.class ) { fields = c.getDeclaredFields(); } else { java.lang.reflect.Field[] fields0 = getFields(c.getSuperclass()); java.lang.reflect.Field[] fields1 = c.getDeclaredFields(); fields = new java.lang.reflect.Field[fields0.length + fields1.length]; System.arraycopy(fields0,fields,fields0.length); System.arraycopy(fields1,fields0.length,fields1.length); } fieldsMap.put(c,fields); return fields;

}

// 调用com.sun.tools.javac.Main类的静态方法compile进行动态编译 int status = com.sun.tools.javac.Main.compile( new String[] { "-d",".",source.getName() } ); if( status != 0 ) { source.delete(); throw new Exception("Compiler exit on " + status); } // 编译得到的字节码将被输出到与包结构相同的一个本地目录,文件名为类名加”.class” String output = "."; int curIndex = -1; int lastIndex = 0; while( (curIndex=pkg.indexOf('.',lastIndex)) != -1 ) { output = output + File.separator + pkg.substring( lastIndex,curIndex ); lastIndex = curIndex + 1; } output = output + File.separator + pkg.substring( lastIndex ); output = output + File.separator + className + ".class"; // 从输出文件中读取字节码,并存入字节数组 File target = new File(output); FileInputStream f = new FileInputStream( target ); byte[] codeSource = new byte[(int)target.length()]; f.read( codeSource ); f.close(); // 删除临时文件 source.delete(); target.delete(); return codeSource;

}

defineClass.setAccessible(true); return (Class)defineClass.invoke( Proxy.class,new Object[] { ProxyEx.class.getClassLoader(),pkg.length()==0 ? cName : pkg+"."+cName,codeSource,new Integer(0),new Integer(codeSource.length) } );

}

// 如果不是代理类,抛异常 if( !Proxy.isProxyClass( proxy.getClass() )) { throw new IllegalArgumentException("Not a proxy instance"); } try

{
// 通过反射获取扩展代理类的调用处理器对象
Field invoker = proxy.getClass().getDeclaredField("handler");
invoker.setAccessible(true);
return (InvocationHandler)invoker.get(proxy);
}
catch(Exception e)
{
throw new IllegalArgumentException("Suspect not a proxy instance");
}
}

numTicketForSale ) { throw new Exception("There is no enough ticket available for sale,only " + numTicketForSale + " ticket(s) left"); } int charge = money - ticketNumber * price; if( charge < 0 ) { throw new Exception("Money is not enough. Still needs " + (-charge) + " RMB."); } numTicketForSale -= ticketNumber; return charge; } }

// 创建扩展调用处理器对象 InvocationHandler handler = new InvocationHandlerEx() { public Object getStub(Class stubClass) { // 仅对可接受的Class类型返回stub实体 if( stubClass.isAssignableFrom(stub.getClass()) ) { return stub; } return null; } public Object invoke(Object proxy,Object[] args) throws Throwable { Object o; try { System.out.println(" >>> Enter method: " + method.getName() ); o = method.invoke(stub,args); } catch(InvocationTargetException e) { throw e.getCause(); } finally { System.out.println(" <<< Exit method: " + method.getName() ); } return o; } }; // 通过ProxyEx构造动态代理 TicketSeller seller = (TicketSeller)ProxyEx.newProxyInstance( TicketBuyer.class.getClassLoader(),new Class[] {TicketSeller.class},handler); // 显示代理类的类型 System.out.println("Ticket Seller Class: " + seller.getClass() + "n"); // 直接访问theme变量,验证代理类变量在对象构造时同步的有效性 System.out.println("Ticket Theme: " + seller.theme + "n"); // 函数访问price信息 System.out.println("Query Ticket Price..."); System.out.println("Ticket Price: " + seller.getTicketPrice() + " RMBn"); // 模拟票务交易 buyTicket(seller,1,200); buyTicket(seller,160); buyTicket(seller,250,30000); // 直接更新theme变量 System.out.println("Updating Ticket Theme...n"); seller.theme = "World Expo 2010 in Shanghai"; // 函数访问theme信息,验证扩展动态代理机制对变量同步的有效性 System.out.println("Query Updated Ticket Theme..."); System.out.println("Updated Ticket Theme: " + seller.getTicketTheme() + "n"); } // 购票函数 protected static void buyTicket(TicketSeller seller,int ticketNumber,int money) { try { System.out.println("Transaction: Order " + ticketNumber + " ticket(s) with " + money + " RMB"); int charge = seller.buy(ticketNumber,money); System.out.println("Transaction: Succeed - Charge is " + charge + " RMBn"); } catch (Exception e) { System.out.println("Transaction: Fail - " + e.getMessage() + "n"); } }

}

Ticket Theme: World Expo 2010

Query Ticket Price...

Enter method: getTicketPrice
<<< Exit method: getTicketPrice
Ticket Price: 180 RMB

Transaction: Order 1 ticket(s) with 200 RMB

Enter method: buy
<<< Exit method: buy
Transaction: Succeed - Charge is 20 RMB

Transaction: Order 1 ticket(s) with 160 RMB

Enter method: buy
<<< Exit method: buy
Transaction: Fail - Money is not enough. Still needs 20 RMB.

Transaction: Order 250 ticket(s) with 30000 RMB

Enter method: buy
<<< Exit method: buy
Transaction: Fail - There is no enough ticket available for sale,only 199 ticket(s) left

Updating Ticket Theme...

Query Updated Ticket Theme...

Enter method: getTicketTheme
<<< Exit method: getTicketTheme
Updated Ticket Theme: World Expo 2010 in Shanghai

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读