Skip to content

标题: 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