揭開動(dòng)態(tài)代理的面紗: Jdk/Cglib動(dòng)態(tài)代理

寫在前面
  1. 代理模式
    代理模式,指的是給目標(biāo)對(duì)象提供一個(gè)代理對(duì)象,并由代理對(duì)象控制對(duì)目標(biāo)對(duì)象的引用。
    為什么要引入這個(gè)代理對(duì)象呢,兩個(gè)目的:
  • 通過代理對(duì)象來間接訪問目標(biāo)對(duì)象,這樣能防止直接訪問目標(biāo)對(duì)象帶來的一些不必要的復(fù)雜性。
    (什么不必要的麻煩呢?例如如果客戶端需要訪問的對(duì)象在服務(wù)端,客戶端直接訪問需要處理網(wǎng)絡(luò)等一系列復(fù)雜的問題,如果使用代理模式那么客戶端只需要和代理打交道,客戶端不需要知道代理怎么和服務(wù)端交互。)
  • 通過代理對(duì)象來訪問目標(biāo)對(duì)象,能通過代理對(duì)象對(duì)原有業(yè)務(wù)進(jìn)行增強(qiáng)。


  1. 代理模式有三種實(shí)現(xiàn)方式:
    靜態(tài)代理,Jdk動(dòng)態(tài)代理,Cglib動(dòng)態(tài)代理。
    其中,靜態(tài)代理和動(dòng)態(tài)代理的區(qū)別在于代理類的生成時(shí)間不同。
靜態(tài)代理

靜態(tài)代理三大要素:

  • 抽象對(duì)象(接口,約定了服務(wù)能夠提供的功能)
  • 真實(shí)對(duì)象(實(shí)現(xiàn)類)
  • 代理對(duì)象

真實(shí)對(duì)象及代理對(duì)象都必須實(shí)現(xiàn)這個(gè)接口
代理對(duì)象必須包含真實(shí)對(duì)象。(它不提供服務(wù),只是服務(wù)的搬運(yùn)工)

面向?qū)ο笤O(shè)計(jì)開發(fā)的幾個(gè)原則之一:
開閉原則(開放-封閉原則):程序?qū)ν鈹U(kuò)展開放,對(duì)修改關(guān)閉。換句話說,當(dāng)需求發(fā)生變化時(shí),我們可以通過添加新模塊來滿足新需求,而不是通過修改原來的實(shí)現(xiàn)代碼來滿足新需求。

靜態(tài)代理作為代理模式的第一個(gè)實(shí)現(xiàn)版本,違反了開閉原則:

  • 讓這個(gè)類的可擴(kuò)展性大打折扣。
  • 可維護(hù)性下降,牽一發(fā),動(dòng)全身。

而動(dòng)態(tài)代理,很好的解決了這個(gè)缺點(diǎn)。

動(dòng)態(tài)代理

何謂動(dòng)態(tài)?動(dòng)態(tài)指的是代理對(duì)象不是固定對(duì)象,而是一個(gè)動(dòng)態(tài)對(duì)象。
動(dòng)態(tài)代理沒有源文件,直接在內(nèi)存生成類的字節(jié)碼文件。

  1. Jdk動(dòng)態(tài)代理
*Tips*

在研究Jdk動(dòng)態(tài)代理之前,先提出幾點(diǎn)疑問:
a. 為什么Jdk動(dòng)態(tài)代理一定要基于接口?
b. 怎么在內(nèi)存生成字節(jié)碼?
c. 生成的字節(jié)碼長(zhǎng)什么樣?
d. 多個(gè)對(duì)象,實(shí)現(xiàn)同一個(gè)接口,若對(duì)這多個(gè)對(duì)象分別進(jìn)行代理,會(huì)生成多個(gè)代理對(duì)象嗎?
e. 多個(gè)對(duì)象,實(shí)現(xiàn)不同接口,若對(duì)這多個(gè)對(duì)象分別進(jìn)行代理,是否會(huì)生成多個(gè)代理對(duì)象呢?

接下來,帶著這幾個(gè)問題來研究Jdk動(dòng)態(tài)代理的底層源碼。

1.1 Jdk動(dòng)態(tài)代理的兩大關(guān)鍵點(diǎn):Proxy類和InvocationHandler接口。
Proxy - 調(diào)度器,生成代理對(duì)象

        JdkProxy dynamicProxy=new JdkProxy(hello);
        Greeting target1=(Greeting) Proxy.newProxyInstance(hello.getClass().getClassLoader(),
                hello.getClass().getInterfaces(), dynamicProxy);

InvocationHandler - 代理對(duì)象到底有什么業(yè)務(wù)能力

public class JdkProxy implements InvocationHandler {

    private Object target;
    
    public JdkProxy(Object obj){
        this.target=obj;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        
        Object result=null;
        before();
        result=method.invoke(target, args);
        after();
        return result;
    }
    public void before(){
        System.out.println("[JdkProxy] Come to someone.");
    }
    public void after(){
        System.out.println("[JdkProxy] Back to his own corner");
    }
}

1.2 探究Proxy類
Proxy類位于java.lang.reflect包中,追蹤Proxy類的newProxyInstance方法,會(huì)定位到ProxyBuilder的如下方法,截取關(guān)鍵部分:

    private static final class ProxyBuilder {
        private static final Unsafe UNSAFE = Unsafe.getUnsafe();

        // prefix for all proxy class names
        private static final String proxyClassNamePrefix = "$Proxy";

        private static Class<?> defineProxyClass(Module m, List<Class<?>> interfaces) {
            //......
            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg.isEmpty()
                                    ? proxyClassNamePrefix + num
                                    : proxyPkg + "." + proxyClassNamePrefix + num;
            //......
            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces.toArray(EMPTY_CLASS_ARRAY), accessFlags);
            try {
                Class<?> pc = UNSAFE.defineClass(proxyName, proxyClassFile,
                                                 0, proxyClassFile.length,
                                                 loader, null);
                reverseProxyCache.sub(pc).putIfAbsent(loader, Boolean.TRUE);
                return pc;
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
}

其中,generateProxyClass就是在內(nèi)存里直接生成字節(jié)碼的地方!它內(nèi)部調(diào)用了generateClassFile方法(參見附錄),這個(gè)方法就會(huì)根據(jù)Class文件的結(jié)構(gòu),來動(dòng)態(tài)拼接出代理對(duì)象的字節(jié)碼。
而UNSAFE.defineClass,就是直接根據(jù)剛才生成的字節(jié)碼,生成Class對(duì)象。

這個(gè)defineClass方法,最終調(diào)用的是一個(gè)native方法。native方法是用C寫的本地方法,直接調(diào)用操作系統(tǒng)的類庫,來進(jìn)行生成Class對(duì)象。

    public native Class<?> defineClass0(String name, byte[] b, int off, int len,
                                        ClassLoader loader,
                                        ProtectionDomain protectionDomain);

1.3 看一看動(dòng)態(tài)生成的字節(jié)碼文件
既然已經(jīng)在內(nèi)存中生成了字節(jié)碼文件,那么就把它保存到硬盤上,滿足一下好奇心,看看里面是個(gè)什么玩意兒吧。
在ProxyGenerator中,有這樣一個(gè)屬性,它詢問是否需要將動(dòng)態(tài)代理的對(duì)象存到硬盤。(這里是基于jdk9的demo,如果用的是其它版本的jdk,那么屬性可以會(huì)略有不同)

    /** debugging flag for saving generated class files */
    private static final boolean saveGeneratedFiles =
        java.security.AccessController.doPrivileged(
            new GetBooleanAction(
      "jdk.proxy.ProxyGenerator.saveGeneratedFiles")).booleanValue();

既然如此,那么只要將這個(gè)屬性設(shè)為true,就能直接查看硬盤上的Class文件了。

System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

果不其然,在項(xiàng)目目錄下,生成了com/sun/proxy子目錄,下面有$Proxy0.class字節(jié)碼文件。這個(gè)類繼承了Proxy類,并且實(shí)現(xiàn)了Greeting接口。

public final class $Proxy0 extends Proxy  implements Greeting  {
}
  //接口代理方法
    public final void doGreet()
    {
        try
        {
            // invocation handler的 invoke方法在這里被調(diào)用
            super.h.invoke(this, m3, null);
            return;
        }
        catch (Error ) { }
        catch (Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

1.4 關(guān)于InvocationHandler
1.4.1 InvocationHandler是一個(gè)接口。

public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

它被作為參數(shù)傳給底層方法newProxyInstance(caller, cons, h)。這個(gè)方法里有一行注釋:

    private static Object newProxyInstance(Class<?> caller, // null if no SecurityManager
                                           Constructor<?> cons,
                                           InvocationHandler h) {
        /*
         * Invoke its constructor with the designated invocation handler.
         */
        //......
    }

1.4.2 追蹤這個(gè)方法,會(huì)發(fā)現(xiàn)最終調(diào)用了一個(gè)native方法,InvocationHandler被作為參數(shù)傳過去了。

    private static native Object newInstance0(Constructor<?> c, Object[] args)
        throws InstantiationException,
               IllegalArgumentException,
               InvocationTargetException;

這個(gè)類實(shí)際做的事情就是:

  • 根據(jù)前面生成的類$Proxy0的字節(jié)碼,來實(shí)例化這個(gè)類
  • 并且,將invocationHandler傳遞給$Proxy0的父類Proxy的構(gòu)造函數(shù),初始化Proxy的屬性h
    /**
     * Constructs a new {@code Proxy} instance from a subclass
     * (typically, a dynamic proxy class) with the specified value
     * for its invocation handler.
     *
     * @param  h the invocation handler for this proxy instance
     *
     * @throws NullPointerException if the given invocation handler, {@code h},
     *         is {@code null}.
     */
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

1.4.3 invoke方法的參數(shù)

  • InvocationHandler接口定義了一個(gè)invoke方法,它接收三個(gè)參數(shù)。
public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;

第二個(gè)參數(shù)method,和第三個(gè)參數(shù)args,指的是代理類被調(diào)用的方法和參數(shù)。
而第一個(gè)參數(shù)proxy,很少會(huì)被用到,其實(shí)它指的就是代理對(duì)象。將代理對(duì)象傳遞進(jìn)來有兩個(gè)用處:
可以通過反射獲取代理對(duì)象的信息。
可以將代理對(duì)象返回進(jìn)行連續(xù)調(diào)用。

  • 用一個(gè)例子感受一下。(截取部分代碼,完整代碼見附錄)
    實(shí)現(xiàn)InvocationHandler的invoke方法。
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        before();
        method.invoke(target, args);
        after();
        System.out.println("JdkProxy.this is : " + this.getClass());
        System.out.println("proxy is : " + proxy.getClass());
        
        return proxy;
    }

通過代理類調(diào)用目標(biāo)方法。

        Meeting videoMeeting = new VideoMeeting();
        dynamicProxy = new JdkProxy(videoMeeting);
        Meeting target4 = (Meeting) Proxy.newProxyInstance(videoMeeting.getClass().getClassLoader(), 
                videoMeeting.getClass().getInterfaces(), dynamicProxy);
        target4.haveMeeting().haveMeeting();

執(zhí)行,通過輸出,可以發(fā)現(xiàn)proxy就是動(dòng)態(tài)生成的代理類$Proxy1,而目標(biāo)方法通過連續(xù)調(diào)用,被代理類調(diào)用了兩次。

[JdkProxy] Come to someone.
Meeting by video .
[JdkProxy] Back to his own corner
JdkProxy.this is : class spring.core.aop.jdk.JdkProxy
proxy is : class com.sun.proxy.$Proxy1
[JdkProxy] Come to someone.
Meeting by video .
[JdkProxy] Back to his own corner
JdkProxy.this is : class spring.core.aop.jdk.JdkProxy
proxy is : class com.sun.proxy.$Proxy1

1.5 為什么Jdk動(dòng)態(tài)代理必須基于接口

  • 這是由jdk動(dòng)態(tài)代理的設(shè)計(jì)決定的
    通過查看動(dòng)態(tài)代理類的代碼,發(fā)現(xiàn)它繼承了Proxy類。而Java是單繼承,不能同時(shí)繼承兩個(gè)類,所以我們需要和想要代理的類建立聯(lián)系,那就只能通過接口來實(shí)現(xiàn)。

  • 那么問題來了,jdk動(dòng)態(tài)代理為什么要設(shè)計(jì)成只允許動(dòng)態(tài)代理接口呢?
    在代理模式中,代理類只做一些額外的攔截處理,實(shí)際處理是轉(zhuǎn)發(fā)到原始類做的。
    如果允許動(dòng)態(tài)代理一個(gè)類,那么代理對(duì)象也會(huì)繼承類的字段,而這些字段實(shí)際上是沒有使用的,因?yàn)榇韺?duì)象只做轉(zhuǎn)發(fā)處理,對(duì)象的字段存取都是在原始對(duì)象上處理,所以如果代理一個(gè)類,對(duì)內(nèi)存空間是一種浪費(fèi)。

1.6 緩存機(jī)制
調(diào)用過程中發(fā)現(xiàn),對(duì)于不同的類做動(dòng)態(tài)代理,生成的代理對(duì)象,有時(shí)候是同一個(gè)對(duì)象。這是因?yàn)楫?dāng)需要生成代理對(duì)象字節(jié)碼之前,會(huì)先查看緩存,是否已經(jīng)存在同樣classLoader&同樣interface的代理對(duì)象,如果存在,則直接復(fù)用緩存中的class字節(jié)碼。

  1. Cglib動(dòng)態(tài)代理
    (Code Generator Library)
*Tips*

同樣,在研究Cglib動(dòng)態(tài)代理之前,同樣提出幾個(gè)問題:
a. 為什么Cglib動(dòng)態(tài)代理不用基于接口?
b. 怎么在內(nèi)存生成字節(jié)碼?
c. 生成的字節(jié)碼長(zhǎng)什么樣?

2.1 Cglib動(dòng)態(tài)代理的兩大關(guān)鍵點(diǎn)

  • Enhancer類
    類似于jdk動(dòng)態(tài)代理中的Proxy類,只不過,Enhancer類既能代理普通的類,也能夠代理接口。
    public <T> T getProxy(Class<T> cls){
        return (T) Enhancer.create(cls, this);
    }
  • MethodInterceptor接口
    重寫接口中的intercept方法對(duì)目標(biāo)方法進(jìn)行攔截。
    @Override
    public Object intercept(Object obj, Method method, Object[] arg,
            MethodProxy proxy) throws Throwable {
        Object result=null;
        try {
            before();
            result= proxy.invokeSuper(obj, arg);
            after();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

2.2 追蹤Enhancer.create()方法
會(huì)發(fā)現(xiàn)Cglib的底層是調(diào)用了ASM開源包,將代理對(duì)象類的class文件加載進(jìn)來,通過修改其字節(jié)碼生成子類來進(jìn)行處理。

    private Object createHelper() {
        preValidate();
        Object key = KEY_FACTORY.newInstance((superclass != null) ? superclass.getName() : null,
                ReflectUtils.getNames(interfaces),
                filter == ALL_ZERO ? null : new WeakCacheKey<CallbackFilter>(filter),
                callbackTypes,
                useFactory,
                interceptDuringConstruction,
                serialVersionUID);
        this.currentKey = key;
        Object result = super.create(key);
        return result;
    }

2.3 關(guān)于MethodInterceptor
方法攔截器

2.4 動(dòng)態(tài)代理對(duì)象的字節(jié)碼文件

public class SayHello$$EnhancerByCGLIB$$4da4ebaf extends SayHello
    implements Factory
{
 }

2.5 當(dāng)被代理方法或者對(duì)象被final修飾

  • 被代理方法被final修飾時(shí),若進(jìn)行代理,則增強(qiáng)無效。
  • 被代理對(duì)象被final修飾時(shí),若進(jìn)行代理,則運(yùn)行時(shí)報(bào)錯(cuò),如下。
Exception in thread "main" java.lang.IllegalArgumentException: Cannot subclass final class spring.core.aop.SayHello
  1. 比較jdk動(dòng)態(tài)代理與cglib動(dòng)態(tài)代理
    3.1 各自局限
    JDK動(dòng)態(tài)代理:只能代理實(shí)現(xiàn)了接口的類。
    Cglib動(dòng)態(tài)代理:由于它的原理是對(duì)指定的目標(biāo)類生成一個(gè)子類,并覆蓋其中方法實(shí)現(xiàn)增強(qiáng),采用了繼承,所以不能對(duì)final修飾的類進(jìn)行代理。

3.2 各自優(yōu)勢(shì)
JDK動(dòng)態(tài)代理:最小化依賴關(guān)系,減少依賴意味著簡(jiǎn)化開發(fā)和維護(hù),由于JDK本身的支持,可能比Cglib更可靠。
Cglib動(dòng)態(tài)代理:可以代理沒有接口的類。并且性能相對(duì)更高。

3.3 性能比較(未考證)
jdk8之前,cglib效率更高。
jdk8及之后,jdk效率更高。

附上部分底層代碼及示例代碼
底層方法

generateClassFile

    private byte[] generateClassFile() {

        /* ============================================================
         * Step 1: Assemble ProxyMethod objects for all methods to
         * generate proxy dispatching code for.
         */

        /*
         * 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 (Class<?> intf : interfaces) {
            for (Method m : intf.getMethods()) {
                addProxyMethod(m, intf);
            }
        }

        /*
         * 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", e);
        }

        if (methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        }
        if (fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        }

        /* ============================================================
         * 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 (Class<?> intf: interfaces) {
            cp.getClass(dotToSlash(intf.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(accessFlags);
                                        // 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 (Class<?> intf : interfaces) {
                dout.writeShort(cp.getClass(
                    dotToSlash(intf.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", e);
        }

        return bout.toByteArray();
    }
完整示例代碼-靜態(tài)代理
public interface Greeting {
    public void doGreet();
}
public class SayHello implements Greeting {
    @Override
    public void doGreet() {
        System.out.println("Greeting by say 'hello' .");
    }
}
public class GreetStaticProxy implements Greeting {

    private Greeting hello;//被代理對(duì)象
    public GreetStaticProxy(Greeting hello){
        this.hello=hello;
    }
    
    @Override
    public void doGreet() {
        before();//執(zhí)行其他操作
        this.hello.doGreet();//調(diào)用目標(biāo)方法
        after();//執(zhí)行其他操作
    }

    public void before(){
        System.out.println("[StaticProxy] Come to someone.");
    }
    public void after(){
        System.out.println("[StaticProxy] Back to his own corner");
    }
}
public class Main {

    public static void main(String[] args) {
        Greeting hello=new SayHello();
        
        //靜態(tài)代理
        GreetStaticProxy staticHelloProxy=new GreetStaticProxy(hello);
        staticHelloProxy.doGreet();
        System.out.println();
    }    

}
完整示例代碼-JDK動(dòng)態(tài)代理
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class JdkProxy implements InvocationHandler {

    private Object target;
    
    public JdkProxy(Object obj){
        this.target=obj;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        before();
        method.invoke(target, args);
        after();
        System.out.println("JdkProxy.this is : " + this.getClass());
        System.out.println("proxy is : " + proxy.getClass());
        
        return proxy;
    }

    
    public Object getTarget() {
        return target;
    }

    public void setTarget(Object target) {
        this.target = target;
    }

    public void before(){
        System.out.println("[JdkProxy] Come to someone.");
    }
    public void after(){
        System.out.println("[JdkProxy] Back to his own corner");
    }
}
import java.lang.reflect.Proxy;

public class Main {
    public static void main(String[] args) {
            System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
        
        Greeting hello=new SayHello();
        Greeting shakeHands=new ShakeHands();
        
        //jdk動(dòng)態(tài)代理
        JdkProxy dynamicProxy=new JdkProxy(hello);
        Greeting target1=(Greeting) Proxy.newProxyInstance(hello.getClass().getClassLoader(),
                hello.getClass().getInterfaces(), dynamicProxy);
        target1.doGreet();
        System.out.println();          
    }    
}
完整示例代碼-CGLIB動(dòng)態(tài)代理
public final class SayHello {
    public final void doGreet() {
        System.out.println("Greeting by say 'hello' .");
    }
}
import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class CglibProxy implements MethodInterceptor {

    public static CglibProxy proxy=new CglibProxy();
    private CglibProxy(){}
    
    public static CglibProxy getInstance(){
        return proxy;
    }
    
    public <T> T getProxy(Class<T> cls){
        return (T) Enhancer.create(cls, this);
    }
    
    @Override
    public Object intercept(Object obj, Method method, Object[] arg,
            MethodProxy proxy) throws Throwable {
        Object result=null;
        try {
            before();
            result= proxy.invokeSuper(obj, arg);
            after();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return result;
    }

    public void before(){
        System.out.println("[cglib] Come to someone.");
    }
    public void after(){
        System.out.println("[cglib] Back to his own corner.");
    }
}
public class Main {

    public static void main(String[] args) {
        
        //cglib代理
        SayHello targetProxy=CglibProxy.getInstance().getProxy(SayHello.class);
        targetProxy.doGreet();
        System.out.println();
        
CglibProxy.getInstance().getInstance().getProxy(KissHello.class).doGreet();    
    }    

}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,739評(píng)論 6 534
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,634評(píng)論 3 419
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,653評(píng)論 0 377
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,063評(píng)論 1 314
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,835評(píng)論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,235評(píng)論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,315評(píng)論 3 442
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,459評(píng)論 0 289
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,000評(píng)論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,819評(píng)論 3 355
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,004評(píng)論 1 370
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,560評(píng)論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,257評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,676評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,937評(píng)論 1 288
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,717評(píng)論 3 393
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,003評(píng)論 2 374

推薦閱讀更多精彩內(nèi)容