标题: Java设计模式之代理模式 创建: 2019-11-29 14:25 更新: 2019-12-02 10:51 链接: https://scz.617.cn/misc/201911291425.txt -------------------------------------------------------------------------- 目录: 1) 静态代理模式 1.1) TicketService.java 1.2) TicketStation.java 1.3) TicketStationProxy.java 1.4) TicketServiceClient.java 2) 动态代理模式 2.1) GeneralInvocationHandler.java 2.2) TicketServiceClient1.java 2.3) GeneralInvocationHandler2.java 2.4) TicketServiceClient2.java 2.5) TicketStation.SellTicket()调用栈回溯 2.6) 获取动态生成的"com.sun.proxy.$Proxy0" 2.7) 反编译动态生成的"com.sun.proxy.$Proxy0" 2.8) 其他方案获取"com.sun.proxy.$Proxy0" 3) 简单对比静态代理模式、动态代理模式 4) cglib动态代理 4.1) GeneralCglibProxy.java 4.2) TicketServiceClient3.java 4.3) TicketStation.SellTicket()调用栈回溯 4.4) 获取动态生成的"TicketStation$$EnhancerByCGLIB$$abea2160" 4.5) 反编译动态生成的类 4.6) 其他方案获取动态生成的类 4.6.1) GeneralCglibProxy4.java 4.6.2) TicketServiceClient4.java 5) 简单对比Java原生动态代理、cglib动态代理 -------------------------------------------------------------------------- 逆向工程中了解目标所用设计模式有助于提纲挈领。代理模式天然具有Hook能力,众 多Java框架使用代理模式。 本文没有直接展示Java动态代理技术与安全、逆向工程相关的应用方式,完全是从正 经程序员的角度提供Java动态代理技术的"Hello World"。如果你是干我这行的,我 觉得你再也找不到一篇比这篇更适合你入门的同类文章。 设计模式这种东西,还是在战争中学习战争比较有意义,照搬固定模式显得生硬,吃 不透背后的设计逻辑。程序员用程序来说话,说一千道一万,不如一套完整示例来得 简单易懂。 1) 静态代理模式 1.1) TicketService.java -------------------------------------------------------------------------- /* * javac -encoding GBK -g TicketService.java * * 票务服务接口 */ public interface TicketService { void SellTicket (); // 出售 void ConsultTicket (); // 咨询 void ReturnTicket (); // 退票 } -------------------------------------------------------------------------- 1.2) TicketStation.java -------------------------------------------------------------------------- /* * javac -encoding GBK -g TicketStation.java */ import java.io.*; /* * 售票站,具体实现票务服务接口,提供具体业务逻辑 */ public class TicketStation implements TicketService { @Override public void SellTicket () { System.out.println( "TicketStation.SellTicket()" ); } @Override public void ConsultTicket () { System.out.println( "TicketStation.ConsultTicket()" ); } @Override public void ReturnTicket () { System.out.println( "TicketStation.ReturnTicket()" ); } } -------------------------------------------------------------------------- 1.3) TicketStationProxy.java -------------------------------------------------------------------------- /* * javac -encoding GBK -g TicketStationProxy.java */ import java.io.*; /* * 代理售票点,可以就近代替售票站提供便民服务。必须与售票站一样,实现票务 * 服务接口,但有些活儿可以不自己干,转发。 */ public class TicketStationProxy implements TicketService { private TicketStation ts; public TicketStationProxy ( TicketStation ts ) { this.ts = ts; } @Override public void SellTicket () { /* * 代理售票点在代理销售过程中可以提供额外服务、收取额外费用,通过 * before()、after()体现 */ before( "TicketStationProxy.SellTicket()" ); /* * 代理售票点转发服务请求至售票站,不需要事必躬亲,但可以自己做, * 比如销售假票、一票多售等等 */ ts.SellTicket(); after( "TicketStationProxy.SellTicket()" ); } @Override public void ConsultTicket () { before( "TicketStationProxy.ConsultTicket()" ); ts.ConsultTicket(); after( "TicketStationProxy.ConsultTicket()" ); } @Override public void ReturnTicket () { before( "TicketStationProxy.ReturnTicket()" ); ts.ReturnTicket(); after( "TicketStationProxy.ReturnTicket()" ); } private void before ( String s ) { System.out.println( "Before " + s ); } private void after ( String s ) { System.out.println( "After " + s ); } } -------------------------------------------------------------------------- 1.4) TicketServiceClient.java -------------------------------------------------------------------------- /* * javac -encoding GBK -g TicketServiceClient.java * java TicketServiceClient */ import java.io.*; /* * 票务服务接口的最终用户,就是我们这些买票人 */ public class TicketServiceClient { public static void main ( String[] argv ) throws Exception { /* * 可以直接去售票站买票,但我们图省事,想就近在代理售票点买票 */ TicketStationProxy tsp = new TicketStationProxy( new TicketStation() ); /* * 调用票务服务接口的售票功能 */ tsp.SellTicket(); /* * 调用票务服务接口的咨询功能,出事了,过年不能回湖南了,我想退票 */ tsp.ConsultTicket(); /* * 调用票务服务接口的退票功能 */ tsp.ReturnTicket(); } } -------------------------------------------------------------------------- $ java TicketServiceClient Before TicketStationProxy.SellTicket() TicketStation.SellTicket() After TicketStationProxy.SellTicket() Before TicketStationProxy.ConsultTicket() TicketStation.ConsultTicket() After TicketStationProxy.ConsultTicket() Before TicketStationProxy.ReturnTicket() TicketStation.ReturnTicket() After TicketStationProxy.ReturnTicket() 有4种角色: 票务服务接口 // 定义接口 售票站 // 实现接口 代理售票点 // 实现同一接口,可转发,可自实现,可增加额外动作 买票人 // 访问接口,可以直接去售票站,也可以去代理售票点 这些设计模式噱头居多,跟传统武术套路动作差不多,我胡说的,请忽略。 2) 动态代理模式 2.1) GeneralInvocationHandler.java -------------------------------------------------------------------------- /* * javac -encoding GBK -g GeneralInvocationHandler.java */ import java.io.*; import java.lang.reflect.*; /* * https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/InvocationHandler.html * * 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. */ public class GeneralInvocationHandler implements InvocationHandler { private Object realobj; public GeneralInvocationHandler ( Object realobj ) { this.realobj = realobj; } /* * This method will be invoked on an invocation handler when a method * is invoked on a proxy instance that it is associated with. */ @Override public Object invoke ( Object proxy, Method method, Object[] args ) throws Throwable { /* * 转发至目标对象 */ Object obj = method.invoke( realobj, args ); return( obj ); } } -------------------------------------------------------------------------- 如果看到谁把GeneralInvocationHandler这种类称为"动态代理类",你可以喷TA一脸, 那是TA英语太差。 GeneralInvocationHandler与Ticket*没有任何静态关系,是个通用实现。因为本例 目的是演示动态代理模式的程序框架,越基本越易抓住要点。 可以让GeneralInvocationHandler与Ticket*紧耦合,在 GeneralInvocationHandler.invoke()中进行额外操作,比如before()、after()之类 的,甚至可以彻底改变预期动作,相当于把静态代理模式中TicketStationProxy的拦 截动作集中到GeneralInvocationHandler.invoke()中。 2.2) TicketServiceClient1.java -------------------------------------------------------------------------- /* * javac -encoding GBK -g TicketServiceClient1.java * java TicketServiceClient1 */ import java.io.*; import java.lang.reflect.*; public class TicketServiceClient1 { public static void main ( String[] argv ) throws Exception { GeneralInvocationHandler gih = new GeneralInvocationHandler( new TicketStation() ); /* * https://docs.oracle.com/javase/8/docs/api/java/lang/reflect/Proxy.html * * Java与C不同,后者续行用反斜杠(\),Java不需要额外的续行符,直接 * 折行即可。 */ TicketService ts = ( TicketService )Proxy.newProxyInstance ( TicketService.class.getClassLoader(), new Class[] { TicketService.class }, gih ); /* * 另一种方式得到ts,写法复杂 */ // ( TicketService )Proxy. // getProxyClass( TicketService.class.getClassLoader(), TicketService.class ). // getConstructor( InvocationHandler.class ). // newInstance( gih ); /* * 就本例而言,会去调用TicketStation.SellTicket() */ ts.SellTicket(); ts.ConsultTicket(); ts.ReturnTicket(); } } -------------------------------------------------------------------------- $ java TicketServiceClient1 TicketStation.SellTicket() TicketStation.ConsultTicket() TicketStation.ReturnTicket() 相比静态代理模式,动态代理模式没有TicketStationProxy,但多了一个 GeneralInvocationHandler。 2.3) GeneralInvocationHandler2.java -------------------------------------------------------------------------- /* * javac -encoding GBK -g GeneralInvocationHandler2.java */ import java.io.*; import java.lang.reflect.*; public class GeneralInvocationHandler2 implements InvocationHandler { private Object realobj; /* * 实现bind(),简化编程,参看TicketServiceClient2.java。函数名任意。 */ public Object bind ( Object realobj ) { this.realobj = realobj; Object obj = Proxy.newProxyInstance ( realobj.getClass().getClassLoader(), /* * https://docs.oracle.com/javase/8/docs/api/java/lang/Class.html * * 返回"Class[]" */ realobj.getClass().getInterfaces(), this ); return( obj ); } @Override public Object invoke ( Object proxy, Method method, Object[] args ) throws Throwable { /* * 转发至目标对象 */ Object obj = method.invoke( realobj, args ); return( obj ); } } -------------------------------------------------------------------------- GeneralInvocationHandler2与Ticket*同样没有任何静态关系,是个通用实现。 2.4) TicketServiceClient2.java -------------------------------------------------------------------------- /* * javac -encoding GBK -g TicketServiceClient2.java * java TicketServiceClient2 */ import java.io.*; public class TicketServiceClient2 { public static void main ( String[] argv ) throws Exception { GeneralInvocationHandler2 gih = new GeneralInvocationHandler2(); /* * GeneralInvocationHandler2实现bind(),简化编程 */ TicketService ts = ( TicketService )gih.bind( new TicketStation() ); ts.SellTicket(); ts.ConsultTicket(); ts.ReturnTicket(); } } -------------------------------------------------------------------------- TicketServiceClient2比TicketServiceClient1更简捷,进一步隐藏不必要暴露的细 节。 2.5) TicketStation.SellTicket()调用栈回溯 $ java -version openjdk version "1.8.0_232" OpenJDK Runtime Environment (build 1.8.0_232-b09) OpenJDK 64-Bit Server VM (build 25.232-b09, mixed mode) $ java -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:8005,server=y,suspend=y TicketServiceClient2 $ jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8005 stop in TicketStation.SellTicket [1] TicketStation.SellTicket (TicketStation.java:14), pc = 0 [2] sun.reflect.NativeMethodAccessorImpl.invoke0 (native method) [3] sun.reflect.NativeMethodAccessorImpl.invoke (NativeMethodAccessorImpl.java:62), pc = 100 [4] sun.reflect.DelegatingMethodAccessorImpl.invoke (DelegatingMethodAccessorImpl.java:43), pc = 6 [5] java.lang.reflect.Method.invoke (Method.java:498), pc = 56 [6] GeneralInvocationHandler2.invoke (GeneralInvocationHandler2.java:37), pc = 6 [7] com.sun.proxy.$Proxy0.SellTicket (null), pc = 9 [8] TicketServiceClient2.main (TicketServiceClient2.java:16), pc = 24 调用栈回溯中出现一个在内存中动态生成的类: com.sun.proxy.$Proxy0 它在设计模式中的地位相当于静态代理模式中的TicketStationProxy,但它是动态生 成的,所以这种技术方案叫动态代理模式。 TicketServiceClient2.java中的ts,其类型正是"com.sun.proxy.$Proxy0"。 查看这个类: class com.sun.proxy.$Proxy0 Class: com.sun.proxy.$Proxy0 extends: java.lang.reflect.Proxy implements: TicketService 显示它的方法: methods com.sun.proxy.$Proxy0 ... TicketService SellTicket() TicketService ConsultTicket() TicketService ReturnTicket() 2.6) 获取动态生成的"com.sun.proxy.$Proxy0" -------------------------------------------------------------------------- /* * javac -encoding GBK -g -XDignore.symbol.file TicketServiceGetTmpProxy0.java * java TicketServiceGetTmpProxy0 TicketServiceTmpProxy0 * * warning: ProxyGenerator is internal proprietary API and may be removed in a future release * * 为了抑制这个编译时警告,Java 8可以指定"-XDignore.symbol.file" */ import java.io.*; /* * http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/misc/ProxyGenerator.java */ import sun.misc.ProxyGenerator; public class TicketServiceGetTmpProxy0 { private static void WriteByte ( String logname, /* * 如果是String,可以调用"somthing".getBytes()得到byte[] */ byte[] log ) { FileOutputStream fos; /* * 非Append模式,不存在时会自动创建 */ try { fos = new FileOutputStream( logname, false ); fos.write( log ); fos.flush(); fos.close(); } catch ( IOException e ) { e.printStackTrace(); } } /* end of WriteByte */ public static void main ( String[] argv ) throws Exception { byte[] buf = ProxyGenerator.generateProxyClass ( argv[0], new Class[] { TicketService.class } ); WriteByte( argv[0] + ".class", buf ); } } -------------------------------------------------------------------------- $ java TicketServiceGetTmpProxy0 TicketServiceTmpProxy0 在当前目录下生成TicketServiceTmpProxy0.class。TicketServiceTmpProxy0这个名 字我随便起的,可以是任意值。 2.7) 反编译动态生成的"com.sun.proxy.$Proxy0" 用JD-GUI查看TicketServiceTmpProxy0.class,对应"com.sun.proxy.$Proxy0"。 -------------------------------------------------------------------------- import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; public final class TicketServiceTmpProxy0 extends Proxy implements TicketService { private static Method m4; private static Method m5; private static Method m3; /* * GeneralInvocationHandler2从此处传进来 */ public TicketServiceTmpProxy0 ( InvocationHandler paramInvocationHandler ) { /* * 设置this.h */ super( paramInvocationHandler ); } public final void ConsultTicket () { try { this.h.invoke( this, m4, null ); return; } ... } public final void ReturnTicket () { try { this.h.invoke( this, m5, null ); return; } ... } public final void SellTicket() { try { this.h.invoke( this, m3, null ); return; } ... } static { try { ... m4 = Class.forName( "TicketService" ).getMethod( "ConsultTicket", new Class[0] ); m5 = Class.forName( "TicketService" ).getMethod( "ReturnTicket", new Class[0] ); m3 = Class.forName( "TicketService" ).getMethod( "SellTicket", new Class[0] ); return; } ... } } -------------------------------------------------------------------------- 2.8) 其他方案获取"com.sun.proxy.$Proxy0" http://hg.openjdk.java.net/jdk8u/jdk8u/jdk/file/jdk8u232-ga/src/share/classes/sun/misc/ProxyGenerator.java 如果系统属性sun.misc.ProxyGenerator.saveGeneratedFiles为true,则内部变量 saveGeneratedFiles为true;generateProxyClass()中发现saveGeneratedFiles为 true时,会自动转储动态生成的类。下面演示另一种办法获取动态生成的 "com.sun.proxy.$Proxy0"。 -------------------------------------------------------------------------- /* * javac -encoding GBK -g -XDignore.symbol.file TicketServiceOtherGetTmpProxy0.java * mkdir -p com/sun/proxy * java TicketServiceOtherGetTmpProxy0 true 0 * java TicketServiceOtherGetTmpProxy0 true 1 TicketServiceTmpProxy0 * ls -l com/sun/proxy/ */ import java.io.*; import sun.misc.ProxyGenerator; public class TicketServiceOtherGetTmpProxy0 { public static void main ( String[] argv ) throws Exception { /* * 如果argv[0]不为"true",generateProxyClass()不会自动转储动态生成 * 的类。 */ System.getProperties().put( "sun.misc.ProxyGenerator.saveGeneratedFiles", argv[0] ); if ( argv[1].equals( "0" ) ) { /* * 在当前目录下生成"com/sun/proxy/$Proxy0.class",要求 * "com/sun/proxy/"事先存在,否则报错 */ GeneralInvocationHandler2 gih = new GeneralInvocationHandler2(); TicketService ts = ( TicketService )gih.bind( new TicketStation() ); } else { /* * 在当前目录下生成"argv[2].class",跟"com/sun/proxy/"没关系 */ byte[] buf = ProxyGenerator.generateProxyClass ( argv[2], new Class[] { TicketService.class } ); } } } -------------------------------------------------------------------------- 上例演示了多种情形,注意看注释。下面两种设置系统属性的方式等效: System.setProperty() System.getProperties().put() 可以用一种更简单的办法获取动态生成的"com.sun.proxy.$Proxy0": $ java -Dsun.misc.ProxyGenerator.saveGeneratedFiles=true TicketServiceClient2 $ ls -l com/sun/proxy/ -rw-rw-r--. 1 scz scz 2179 Nov 28 17:42 $Proxy0.class 喜欢逆向工程的,记住这个方案。 3) 简单对比静态代理模式、动态代理模式 静态代理模式 动态代理模式 TicketService.java TicketService.java TicketStation.java TicketStation.java TicketStationProxy.java com.sun.proxy.$Proxy0 and GeneralInvocationHandler2.java TicketServiceClient.java TicketServiceClient2.java 前两行双方都一样,重点是第三行不同。静态代理模式需要程序员自己实现 TicketStationProxy.java,动态代理模式会在运行时动态生成 com.sun.proxy.$Proxy0,同时动态代理模式需要程序员自己实现 GeneralInvocationHandler2.java。至于第四行的区别,那是自然而来的。 4) cglib动态代理 参看: cglib https://github.com/cglib/cglib 前面演示的动态代理是Java原生方案,有个限制,要求目标必须实现了某个接口,比 如TicketStation实现TicketService。cglib动态代理则没有这个限制,即使 TicketStation没有实现某个接口,也能拦截。外面很多cglib示例太复杂,这里继续 用TicketService、TicketStation为目标,给一个简洁示例。 4.1) GeneralCglibProxy.java -------------------------------------------------------------------------- /* * javac -encoding GBK -g -cp cglib-nodep-3.3.0.jar GeneralCglibProxy.java */ import java.io.*; import java.lang.reflect.Method; import net.sf.cglib.proxy.*; public class GeneralCglibProxy implements MethodInterceptor { /* * 实现bind(),简化编程,参看TicketServiceClient3.java。函数名任意。 */ public Object bind ( Class clazz ) { Enhancer e = new Enhancer(); e.setSuperclass( clazz ); e.setCallback( this ); return( e.create() ); } /* * 相当于GeneralInvocationHandler2.java中的invoke(),是个通用拦截点。 */ @Override public Object intercept ( Object proxy, Method method, Object[] args, MethodProxy fastMethod ) throws Throwable { before( "GeneralCglibProxy.intercept():" + fastMethod + "=>" + method ); /* * 转发至目标对象 */ Object obj = fastMethod.invokeSuper( proxy, args ); after( "GeneralCglibProxy.intercept():" + fastMethod + "=>" + method ); return( obj ); } private void before ( String s ) { System.out.println( "Before " + s ); } private void after ( String s ) { System.out.println( "After " + s ); } } -------------------------------------------------------------------------- GeneralCglibProxy与Ticket*没有任何静态关系,是个通用实现,但可以让 GeneralCglibProxy与Ticket*紧耦合。 4.2) TicketServiceClient3.java -------------------------------------------------------------------------- /* * javac -encoding GBK -g -cp "cglib-nodep-3.3.0.jar:." TicketServiceClient3.java * java -cp "cglib-nodep-3.3.0.jar:." TicketServiceClient3 */ import java.io.*; public class TicketServiceClient3 { public static void main ( String[] argv ) throws Exception { /* * GeneralCglibProxy的相当于GeneralInvocationHandler2 */ GeneralCglibProxy gcp = new GeneralCglibProxy(); /* * GeneralCglibProxy实现bind(),简化编程 */ TicketService ts = ( TicketService )gcp.bind( TicketStation.class ); ts.SellTicket(); ts.ConsultTicket(); ts.ReturnTicket(); } } -------------------------------------------------------------------------- TicketService的存在是不必要的,代码写成这样是为了最大限度接近 TicketServiceClient2.java,更容易对比差异所在。 $ java -cp "cglib-nodep-3.3.0.jar:." TicketServiceClient3 Before GeneralCglibProxy.intercept():net.sf.cglib.proxy.MethodProxy@5a2e4553=>public void TicketStation.SellTicket() TicketStation.SellTicket() After GeneralCglibProxy.intercept():net.sf.cglib.proxy.MethodProxy@5a2e4553=>public void TicketStation.SellTicket() Before GeneralCglibProxy.intercept():net.sf.cglib.proxy.MethodProxy@7e32c033=>public void TicketStation.ConsultTicket() TicketStation.ConsultTicket() After GeneralCglibProxy.intercept():net.sf.cglib.proxy.MethodProxy@7e32c033=>public void TicketStation.ConsultTicket() Before GeneralCglibProxy.intercept():net.sf.cglib.proxy.MethodProxy@7ab2bfe1=>public void TicketStation.ReturnTicket() TicketStation.ReturnTicket() After GeneralCglibProxy.intercept():net.sf.cglib.proxy.MethodProxy@7ab2bfe1=>public void TicketStation.ReturnTicket() 4.3) TicketStation.SellTicket()调用栈回溯 $ java -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:8005,server=y,suspend=y -cp "cglib-nodep-3.3.0.jar:." TicketServiceClient3 $ jdb -connect com.sun.jdi.SocketAttach:hostname=127.0.0.1,port=8005 stop in TicketStation.SellTicket [1] TicketStation.SellTicket (TicketStation.java:14), pc = 0 [2] TicketStation$$EnhancerByCGLIB$$abea2160.CGLIB$SellTicket$0 (null), pc = 1 [3] TicketStation$$EnhancerByCGLIB$$abea2160$$FastClassByCGLIB$$68e8c394.invoke (null), pc = 282 [4] net.sf.cglib.proxy.MethodProxy.invokeSuper (MethodProxy.java:228), pc = 19 [5] GeneralCglibProxy.intercept (GeneralCglibProxy.java:37), pc = 37 [6] TicketStation$$EnhancerByCGLIB$$abea2160.SellTicket (null), pc = 31 [7] TicketServiceClient3.main (TicketServiceClient3.java:19), pc = 19 调用栈回溯中出现几个在内存中动态生成的类: TicketStation$$EnhancerByCGLIB$$abea2160 TicketStation$$EnhancerByCGLIB$$abea2160$$FastClassByCGLIB$$68e8c394 相当于之前讲过的"com.sun.proxy.$Proxy0"。 4.4) 获取动态生成的"TicketStation$$EnhancerByCGLIB$$abea2160" 设置系统属性"cglib.debugLocation"即可转储动态生成的类: $ java -Dcglib.debugLocation=/tmp -cp "cglib-nodep-3.3.0.jar:." TicketServiceClient3 $ ls -l /tmp/TicketStation* -rw-rw-r--. 1 scz scz 5852 Nov 29 12:02 /tmp/TicketStation$$EnhancerByCGLIB$$abea2160.class -rw-rw-r--. 1 scz scz 7177 Nov 29 12:02 /tmp/TicketStation$$EnhancerByCGLIB$$abea2160$$FastClassByCGLIB$$68e8c394.class -rw-rw-r--. 1 scz scz 2579 Nov 29 12:02 /tmp/TicketStation$$FastClassByCGLIB$$9d2b1868.class 4.5) 反编译动态生成的类 依次反编译动态生成的三个类,其中TicketStation$$FastClassByCGLIB$$9d2b1868 是干啥的,没有深究。下文做了删减,重点关注SellTicket相关代码。 -------------------------------------------------------------------------- public class TicketStation$$EnhancerByCGLIB$$abea2160 extends TicketStation implements Factory { private boolean CGLIB$BOUND; public static Object CGLIB$FACTORY_DATA; private static final ThreadLocal CGLIB$THREAD_CALLBACKS; private static final Callback[] CGLIB$STATIC_CALLBACKS; private MethodInterceptor CGLIB$CALLBACK_0; private static Object CGLIB$CALLBACK_FILTER; private static final Method CGLIB$SellTicket$0$Method; private static final MethodProxy CGLIB$SellTicket$0$Proxy; private static final Object[] CGLIB$emptyArgs; private static final Method CGLIB$ReturnTicket$1$Method; private static final MethodProxy CGLIB$ReturnTicket$1$Proxy; private static final Method CGLIB$ConsultTicket$2$Method; private static final MethodProxy CGLIB$ConsultTicket$2$Proxy; private static final Method CGLIB$equals$3$Method; private static final MethodProxy CGLIB$equals$3$Proxy; private static final Method CGLIB$toString$4$Method; private static final MethodProxy CGLIB$toString$4$Proxy; private static final Method CGLIB$hashCode$5$Method; private static final MethodProxy CGLIB$hashCode$5$Proxy; private static final Method CGLIB$clone$6$Method; private static final MethodProxy CGLIB$clone$6$Proxy; static void CGLIB$STATICHOOK1() { final Method[] methods2 = ReflectUtils.findMethods(new String[] { "SellTicket", "()V", "ReturnTicket", "()V", "ConsultTicket", "()V" }, (forName3 = Class.forName("TicketStation")).getDeclaredMethods()); CGLIB$SellTicket$0$Method = methods2[0]; CGLIB$SellTicket$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "SellTicket", "CGLIB$SellTicket$0"); CGLIB$ReturnTicket$1$Method = methods2[1]; CGLIB$ReturnTicket$1$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "ReturnTicket", "CGLIB$ReturnTicket$1"); CGLIB$ConsultTicket$2$Method = methods2[2]; CGLIB$ConsultTicket$2$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "ConsultTicket", "CGLIB$ConsultTicket$2"); } final void CGLIB$SellTicket$0() { /* * 父类是TicketStation,此处去调TicketStation.SellTicket() */ super.SellTicket(); } /* * TicketServiceClient3先调到这个位置,并不会从此函数中直接去调 * TicketStation.SellTicket(),因为需要给GeneralCglibProxy.intercept() * 机会,否则怎么拦截。 */ public final void SellTicket() { MethodInterceptor cglib$CALLBACK_2; MethodInterceptor cglib$CALLBACK_0; if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) { CGLIB$BIND_CALLBACKS(this); cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0); } if (cglib$CALLBACK_0 != null) { /* * 去调GeneralCglibProxy.intercept() */ cglib$CALLBACK_2.intercept((Object)this, TicketStation$$EnhancerByCGLIB$$abea2160.CGLIB$SellTicket$0$Method, TicketStation$$EnhancerByCGLIB$$abea2160.CGLIB$emptyArgs, TicketStation$$EnhancerByCGLIB$$abea2160.CGLIB$SellTicket$0$Proxy); return; } super.SellTicket(); } public static MethodProxy CGLIB$findMethodProxy(final Signature signature) { final String string = signature.toString(); switch (string.hashCode()) { case -1338220137: { if (string.equals("SellTicket()V")) { return TicketStation$$EnhancerByCGLIB$$abea2160.CGLIB$SellTicket$0$Proxy; } break; } } return null; } static { CGLIB$STATICHOOK1(); } } -------------------------------------------------------------------------- public class TicketStation$$EnhancerByCGLIB$$abea2160$$FastClassByCGLIB$$68e8c394 extends FastClass { public int getIndex(final Signature signature) { final String string = signature.toString(); switch (string.hashCode()) { case -1338220137: { if (string.equals("SellTicket()V")) { return 0; } break; } case -1126423064: { if (string.equals("CGLIB$SellTicket$0()V")) { return 11; } break; } } return -1; } public int getIndex(final String s, final Class[] array) { Label_1255: { switch (s.hashCode()) { case 1815919902: { if (!s.equals("SellTicket")) { break; } switch (array.length) { case 0: { return 0; } default: { break Label_1255; } } break; } case 2110177901: { if (!s.equals("CGLIB$SellTicket$0")) { break; } switch (array.length) { case 0: { return 11; } default: { break Label_1255; } } break; } } } return -1; } public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException { final TicketStation$$EnhancerByCGLIB$$abea2160 ticketStation$$EnhancerByCGLIB$$abea2160 = (TicketStation$$EnhancerByCGLIB$$abea2160)o; try { switch (n) { case 0: { ticketStation$$EnhancerByCGLIB$$abea2160.SellTicket(); return null; } case 11: { /* * 去调TicketStation$$EnhancerByCGLIB$$abea2160.CGLIB$SellTicket$0() */ ticketStation$$EnhancerByCGLIB$$abea2160.CGLIB$SellTicket$0(); return null; } } } } } -------------------------------------------------------------------------- public class TicketStation$$FastClassByCGLIB$$9d2b1868 extends FastClass { public int getIndex(final Signature signature) { final String string = signature.toString(); switch (string.hashCode()) { case -1338220137: { if (string.equals("SellTicket()V")) { return 0; } break; } } return -1; } public int getIndex(final String s, final Class[] array) { Label_0294: { switch (s.hashCode()) { case 1815919902: { if (!s.equals("SellTicket")) { break; } switch (array.length) { case 0: { return 0; } default: { break Label_0294; } } break; } } } return -1; } public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException { final TicketStation ticketStation = (TicketStation)o; try { switch (n) { case 0: { ticketStation.SellTicket(); return null; } } } } public Object newInstance(final int n, final Object[] array) throws InvocationTargetException { try { switch (n) { case 0: { return new TicketStation(); } } } } } -------------------------------------------------------------------------- 4.6) 其他方案获取动态生成的类 4.6.1) GeneralCglibProxy4.java -------------------------------------------------------------------------- /* * javac -encoding GBK -g -cp cglib-nodep-3.3.0.jar GeneralCglibProxy4.java */ import java.io.*; import java.lang.reflect.Method; import net.sf.cglib.proxy.*; import net.sf.cglib.core.DefaultGeneratorStrategy; public class GeneralCglibProxy4 implements MethodInterceptor { /* * 用于递增文件名序号 */ private int count = 0; private void WriteByte ( String logname, byte[] log ) { FileOutputStream fos; try { /* * 非追加模式 */ fos = new FileOutputStream( logname, false ); fos.write( log ); fos.flush(); fos.close(); } catch ( IOException e ) { e.printStackTrace(); } } public Object bind ( Class clazz, String basename ) { Enhancer e = new Enhancer(); e.setSuperclass( clazz ); e.setCallback( this ); if ( basename != null ) { e.setStrategy ( new DefaultGeneratorStrategy() { protected byte[] transform ( byte[] b ) { WriteByte( basename + "_" + count + ".class", b ); count++; return( b ); } } ); } return( e.create() ); } @Override public Object intercept ( Object proxy, Method method, Object[] args, MethodProxy fastMethod ) throws Throwable { Object obj = fastMethod.invokeSuper( proxy, args ); return( obj ); } } -------------------------------------------------------------------------- 上例中transform()可以获取动态类的字节码,将之写入文件系统即可。显然此处可 以做任何动作。 4.6.2) TicketServiceClient4.java -------------------------------------------------------------------------- /* * javac -encoding GBK -g -cp "cglib-nodep-3.3.0.jar:." TicketServiceClient4.java * java -cp "cglib-nodep-3.3.0.jar:." TicketServiceClient4 TicketStationTmpProxy */ import java.io.*; public class TicketServiceClient4 { public static void main ( String[] argv ) throws Exception { GeneralCglibProxy4 gcp = new GeneralCglibProxy4(); TicketService ts = ( TicketService )gcp.bind( TicketStation.class, argv[0] ); ts.SellTicket(); ts.ConsultTicket(); ts.ReturnTicket(); } } -------------------------------------------------------------------------- $ java -cp "cglib-nodep-3.3.0.jar:." TicketServiceClient4 TicketStationTmpProxy 将在当前目录下生成若干TicketStationTmpProxy_n.class。TicketStationTmpProxy 是我随便起的名,可以是任意值。 $ ls -l TicketStationTmpProxy* -rw-rw-r--. 1 scz scz 5852 Nov 29 14:02 TicketStationTmpProxy_0.class -rw-rw-r--. 1 scz scz 2579 Nov 29 14:02 TicketStationTmpProxy_1.class -rw-rw-r--. 1 scz scz 7177 Nov 29 14:02 TicketStationTmpProxy_2.class 5) 简单对比Java原生动态代理、cglib动态代理 Java原生动态代理 cglib动态代理 TicketService.java(必要) TicketService.java(不必要) TicketStation.java TicketStation.java 动态生成类 动态生成类 com.sun.proxy.$Proxy0 TicketStation$$EnhancerByCGLIB$$abea2160 TicketStation$$FastClassByCGLIB$$9d2b1868 TicketStation$$EnhancerByCGLIB$$abea2160$$FastClassByCGLIB$$68e8c394 GeneralInvocationHandler2.java GeneralCglibProxy.java TicketServiceClient2.java TicketServiceClient3.java