1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        Java的三種代理模式(Spring動(dòng)態(tài)代理對(duì)象)

        共 68285字,需瀏覽 137分鐘

         ·

        2021-08-04 02:35

        點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

        優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

        1.代理模式

        代理(Proxy)是一種設(shè)計(jì)模式,提供了對(duì)目標(biāo)對(duì)象另外的訪問方式;即通過代理對(duì)象訪問目標(biāo)對(duì)象.這樣做的好處是:可以在目標(biāo)對(duì)象實(shí)現(xiàn)的基礎(chǔ)上,增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對(duì)象的功能.
        這里使用到編程中的一個(gè)思想:不要隨意去修改別人已經(jīng)寫好的代碼或者方法,如果需改修改,可以通過代理的方式來擴(kuò)展該方法

        舉個(gè)例子來說明代理的作用:假設(shè)我們想邀請(qǐng)一位明星,那么并不是直接連接明星,而是聯(lián)系明星的經(jīng)紀(jì)人,來達(dá)到同樣的目的.明星就是一個(gè)目標(biāo)對(duì)象,他只要負(fù)責(zé)活動(dòng)中的節(jié)目,而其他瑣碎的事情就交給他的代理人(經(jīng)紀(jì)人)來解決.這就是代理思想在現(xiàn)實(shí)中的一個(gè)例子

        用圖表示如下:

        代理模式的關(guān)鍵點(diǎn)是:代理對(duì)象與目標(biāo)對(duì)象.代理對(duì)象是對(duì)目標(biāo)對(duì)象的擴(kuò)展,并會(huì)調(diào)用目標(biāo)對(duì)象

        1.1.靜態(tài)代理(類似于裝飾者模式)

        靜態(tài)代理在使用時(shí),需要定義接口或者父類,被代理對(duì)象與代理對(duì)象一起實(shí)現(xiàn)相同的接口或者是繼承相同父類.

        下面舉個(gè)案例來解釋:
        模擬保存動(dòng)作,定義一個(gè)保存動(dòng)作的接口:IUserDao.java,然后目標(biāo)對(duì)象實(shí)現(xiàn)這個(gè)接口的方法UserDao.java,此時(shí)如果使用靜態(tài)代理方 式,就需要在代理對(duì)象(UserDaoProxy.java)中也實(shí)現(xiàn)IUserDao接口.調(diào)用的時(shí)候通過調(diào)用代理對(duì)象的方法來調(diào)用目標(biāo)對(duì)象.
        需要注意的是,代理對(duì)象與目標(biāo)對(duì)象要實(shí)現(xiàn)相同的接口,然后通過調(diào)用相同的方法來調(diào)用目標(biāo)對(duì)象的方法

        代碼示例:
        接口:IUserDao.java

        /**
         * 接口
         */
        public interface IUserDao {

            void save();
        }

        目標(biāo)對(duì)象:UserDao.java

        /**
         * 接口實(shí)現(xiàn)
         * 目標(biāo)對(duì)象
         */
        public class UserDao implements IUserDao {
            public void save() {
                System.out.println("----已經(jīng)保存數(shù)據(jù)!----");
            }
        }

        代理對(duì)象:UserDaoProxy.java

        /**
         * 代理對(duì)象,靜態(tài)代理
         */
        public class UserDaoProxy implements IUserDao{
            //接收保存目標(biāo)對(duì)象
            private IUserDao target;
            public UserDaoProxy(IUserDao target){
                this.target=target;
            }

            public void save() {
                System.out.println("開始事務(wù)...");
                target.save();//執(zhí)行目標(biāo)對(duì)象的方法
                System.out.println("提交事務(wù)...");
            }
        }

        測試類:App.java

        /**
         * 測試類
         */
        public class App {
            public static void main(String[] args) {
                //目標(biāo)對(duì)象
                UserDao target = new UserDao();

                //代理對(duì)象,把目標(biāo)對(duì)象傳給代理對(duì)象,建立代理關(guān)系
                UserDaoProxy proxy = new UserDaoProxy(target);

                proxy.save();//執(zhí)行的是代理的方法
            }
        }

        靜態(tài)代理總結(jié):
        1.可以做到在不修改目標(biāo)對(duì)象的功能前提下,對(duì)目標(biāo)功能擴(kuò)展.
        2.缺點(diǎn):

        • 因?yàn)榇韺?duì)象需要與目標(biāo)對(duì)象實(shí)現(xiàn)一樣的接口,所以會(huì)有很多代理類,類太多.同時(shí),一旦接口增加方法,目標(biāo)對(duì)象與代理對(duì)象都要維護(hù).

        如何解決靜態(tài)代理中的缺點(diǎn)呢?答案是可以使用動(dòng)態(tài)代理方式

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

        動(dòng)態(tài)代理有以下特點(diǎn):
        1.代理對(duì)象,不需要實(shí)現(xiàn)接口
        2.代理對(duì)象的生成,是利用JDK的API,動(dòng)態(tài)的在內(nèi)存中構(gòu)建代理對(duì)象(需要我們指定創(chuàng)建代理對(duì)象/目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型)
        3.動(dòng)態(tài)代理也叫做:JDK代理,接口代理

        JDK中生成代理對(duì)象的API
        代理類所在包:java.lang.reflect.Proxy
        JDK實(shí)現(xiàn)代理只需要使用newProxyInstance方法,但是該方法需要接收三個(gè)參數(shù),完整的寫法是:

        static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )

        注意該方法是在Proxy類中是靜態(tài)方法,且接收的三個(gè)參數(shù)依次為:

        • ClassLoader loader,:指定當(dāng)前目標(biāo)對(duì)象使用類加載器,獲取加載器的方法是固定的

        • Class<?>[] interfaces,:目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類型,使用泛型方式確認(rèn)類型

        • InvocationHandler h:事件處理,執(zhí)行目標(biāo)對(duì)象的方法時(shí),會(huì)觸發(fā)事件處理器的方法,會(huì)把當(dāng)前執(zhí)行目標(biāo)對(duì)象的方法作為參數(shù)傳入

        代碼示例:
        接口類IUserDao.java以及接口實(shí)現(xiàn)類,目標(biāo)對(duì)象UserDao是一樣的,沒有做修改.在這個(gè)基礎(chǔ)上,增加一個(gè)代理工廠類 (ProxyFactory.java),將代理類寫在這個(gè)地方,然后在測試類(需要使用到代理的代碼)中先建立目標(biāo)對(duì)象和代理對(duì)象的聯(lián)系,然后代用代理 對(duì)象的中同名方法

        接口和默認(rèn)實(shí)現(xiàn):

        package aop;

        public interface IUserDao {

            void save();

            void delete();

            void saveAndDelete();
        }

        實(shí)現(xiàn)類:

        package aop;

        public class UserDao implements IUserDao {

            public void save() {
                System.out.println("save");
            }

            public void delete() {
                System.out.println("delete");
            }

            public void saveAndDelete() {
                save();

                delete();
            }
        }

        代理工廠類:ProxyFactory.java

        package aop;

        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Method;
        import java.lang.reflect.Proxy;

        /**
         * 創(chuàng)建動(dòng)態(tài)代理對(duì)象
         * 動(dòng)態(tài)代理不需要實(shí)現(xiàn)接口,但是需要指定接口類型
         */
        public class ProxyFactory {

            //維護(hù)一個(gè)目標(biāo)對(duì)象
            private Object target;

            public ProxyFactory(Object target) {
                this.target = target;
            }

            //給目標(biāo)對(duì)象生成代理對(duì)象
            public Object getProxyInstance() {
                return Proxy.newProxyInstance(
                        target.getClass().getClassLoader(),
                        target.getClass().getInterfaces(),
                        new InvocationHandler() {
                            @Override
                            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                                System.out.println("開始事務(wù)2");
                                //運(yùn)用反射執(zhí)行目標(biāo)對(duì)象方法
                                Object returnValue = method.invoke(target, args);
                                System.out.println("提交事務(wù)2");
                                return returnValue;
                            }
                        }
                );
            }

        }

        測試類:

        package aop;

        /**
         * @author: 喬利強(qiáng)
         * @date: 2020/12/11 19:55
         * @description:
         */
        public class Client {

            public static void main(String[] args) {
                // 目標(biāo)對(duì)象
                UserDao target = new UserDao();
                // class aop.UserDao
                System.out.println(target.getClass());

                // 給目標(biāo)對(duì)象,創(chuàng)建代理對(duì)象
                IUserDao proxy = (IUserDao) new ProxyFactory(target).getProxyInstance();
                // class $Proxy0   內(nèi)存中動(dòng)態(tài)生成的代理對(duì)象
                System.out.println(proxy.getClass());

                proxy.save();

                System.out.println("===1===");

                proxy.delete();

                System.out.println("===2===");

                proxy.saveAndDelete();
            }
        }

        結(jié)果:

        class aop.UserDao
        class com.sun.proxy.$Proxy0
        開始事務(wù)2
        save
        提交事務(wù)2
        ===1===
        開始事務(wù)2
        delete
        提交事務(wù)2
        ===2===
        開始事務(wù)2
        save
        delete
        提交事務(wù)2

           可以看出saveAndDelete 內(nèi)部調(diào)用save和delete方法沒有走攔截到的方法,這個(gè)很好理解,內(nèi)部調(diào)用直接走的方法調(diào)用,沒用代理對(duì)象。

        如果我們想查看生產(chǎn)的代理類:

        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true" );

        總結(jié):
          代理對(duì)象不需要實(shí)現(xiàn)接口,但是目標(biāo)對(duì)象一定要實(shí)現(xiàn)接口,否則不能用動(dòng)態(tài)代理

        1.3.Cglib代理(基于繼承的方式實(shí)現(xiàn))

        pom如下:

              <!-- https://mvnrepository.com/artifact/cglib/cglib -->
                <dependency>
                    <groupId>cglib</groupId>
                    <artifactId>cglib</artifactId>
                    <version>3.2.12</version>
                </dependency>

          上面的靜態(tài)代理和動(dòng)態(tài)代理模式都是要求目標(biāo)對(duì)象是實(shí)現(xiàn)一個(gè)接口的目標(biāo)對(duì)象,但是有時(shí)候目標(biāo)對(duì)象只是一個(gè)單獨(dú)的對(duì)象,并沒有實(shí)現(xiàn)任何的接口,這個(gè)時(shí)候就可以使用以目標(biāo)對(duì)象子類的方式類實(shí)現(xiàn)代理,這種方法就叫做:Cglib代理

        Cglib代理,也叫作子類代理,它是在內(nèi)存中構(gòu)建一個(gè)子類對(duì)象從而實(shí)現(xiàn)對(duì)目標(biāo)對(duì)象功能的擴(kuò)展.

        • JDK的動(dòng)態(tài)代理有一個(gè)限制,就是使用動(dòng)態(tài)代理的對(duì)象必須實(shí)現(xiàn)一個(gè)或多個(gè)接口,如果想代理沒有實(shí)現(xiàn)接口的類,就可以使用Cglib實(shí)現(xiàn).

        • Cglib是一個(gè)強(qiáng)大的高性能的代碼生成包,它可以在運(yùn)行期擴(kuò)展java類與實(shí)現(xiàn)java接口.它廣泛的被許多AOP的框架使用,例如Spring AOP和synaop,為他們提供方法的interception(攔截)

        • Cglib包的底層是通過使用一個(gè)小而塊的字節(jié)碼處理框架ASM來轉(zhuǎn)換字節(jié)碼并生成新的類.不鼓勵(lì)直接使用ASM,因?yàn)樗竽惚仨殞?duì)JVM內(nèi)部結(jié)構(gòu)包括class文件的格式和指令集都很熟悉.

        Cglib子類代理實(shí)現(xiàn)方法:
        1.需要引入cglib的jar文件,但是Spring的核心包中已經(jīng)包括了Cglib功能,所以直接引入
        pring-core-3.2.5.jar即可.
        2.引入功能包后,就可以在內(nèi)存中動(dòng)態(tài)構(gòu)建子類
        3.代理的類不能為final,否則報(bào)錯(cuò)
        4.目標(biāo)對(duì)象的方法如果為final/static,那么就不會(huì)被攔截,即不會(huì)執(zhí)行目標(biāo)對(duì)象額外的業(yè)務(wù)方法.

        5.如果方法為static,private則無法進(jìn)行代理。

        代碼:

        dao和接口同上面JDK代理。

        Cglib代理工廠

        package aop;

        import net.sf.cglib.proxy.MethodInterceptor;
        import net.sf.cglib.proxy.MethodProxy;

        import java.lang.reflect.Method;

        /**
         * @author: 喬利強(qiáng)
         * @date: 2020/12/11 20:05
         * @description:
         */
        public class CglibProxyFactory implements MethodInterceptor {

            public CglibProxyFactory() {
            }

            /**
             * 1、代理對(duì)象;2、委托類方法;3、方法參數(shù);4、代理方法的MethodProxy對(duì)象。
             *
             * @param o
             * @param method
             * @param objects
             * @param methodProxy
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("事務(wù)開始......" + method.getName());
                Object o1 = methodProxy.invokeSuper(o, objects);
                System.out.println("事務(wù)結(jié)束......." + method.getName());
                return o1;
            }
        }

        測試類:

        package aop;

        import net.sf.cglib.core.DebuggingClassWriter;
        import net.sf.cglib.proxy.Enhancer;

        /**
         * @author: 喬利強(qiáng)
         * @date: 2020/12/11 20:07
         * @description:
         */
        public class CglibClient {

            public static void main(String[] args) {
                //在指定目錄下生成動(dòng)態(tài)代理類,我們可以反編譯看一下里面到底是一些什么東西
                System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "G:/proxy");

                CglibProxyFactory cglibProxy = new CglibProxyFactory();
                //jdk需要提供接口,cglib需要是非私有類,且不能處理final關(guān)鍵字修飾的方法
                Enhancer enhancer = new Enhancer();
                //設(shè)置父類
                enhancer.setSuperclass(UserDao.class);
                //設(shè)置回調(diào)對(duì)象
                enhancer.setCallback(cglibProxy);

                UserDao proxy = (UserDao) enhancer.create();
                proxy.save();

                System.out.println("===1===");

                proxy.delete();

                System.out.println("===2===");

                proxy.saveAndDelete();
            }
        }

        結(jié)果:   可以看出通過方法內(nèi)部調(diào)用也走了代理。這個(gè)很好理解相當(dāng)于是調(diào)用的是繼承類的方法。

         事務(wù)開始......save

        save

        事務(wù)結(jié)束.......save

        ===1===

        事務(wù)開始......delete

        delete

        事務(wù)結(jié)束.......delete

        ===2===

        事務(wù)開始......saveAndDelete

        事務(wù)開始......save

        save

        事務(wù)結(jié)束.......save

        事務(wù)開始......delete

        delete

        事務(wù)結(jié)束.......delete

        事務(wù)結(jié)束.......saveAndDelete

         

        關(guān)于cglib動(dòng)態(tài)代理中invokeSuper和invoke的區(qū)別參考:https://blog.csdn.net/z69183787/article/details/106878203/

        補(bǔ)充:在Spring的AOP中自調(diào)用AOP無效。簡單研究了下,是調(diào)用invoke的效果,如下:

        1.修改代理方法

        package aop;

        import net.sf.cglib.proxy.MethodInterceptor;
        import net.sf.cglib.proxy.MethodProxy;

        import java.lang.reflect.Method;

        /**
         * @Author: qlq
         * @Description
         * @Date: 20:23 2021/1/24
         */
        public class CglibProxyFactory  implements MethodInterceptor {

            private Object target;

            public CglibProxyFactory(Object target) {
                super();
                this.target = target;
            }

            /**
             * 1、代理對(duì)象;2、委托類方法;3、方法參數(shù);4、代理方法的MethodProxy對(duì)象。
             *
             * @param o
             * @param method
             * @param objects
             * @param methodProxy
             * @return
             * @throws Throwable
             */
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                System.out.println("事務(wù)開始......" + method.getName());
                Object o1 = methodProxy.invoke(target, objects);
                System.out.println("事務(wù)結(jié)束......." + method.getName());
                return o1;
            }
        }

        2.測試類如下:

        package aop;

        import net.sf.cglib.core.DebuggingClassWriter;
        import net.sf.cglib.proxy.Enhancer;

        /**
         * @Author: qlq
         * @Description
         * @Date: 20:26 2021/1/24
         */
        public class CglibClient {

            public static void main(String[] args) {
                //在指定目錄下生成動(dòng)態(tài)代理類,我們可以反編譯看一下里面到底是一些什么東西
                System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "G:/proxy");

                UserDao userDao = new UserDao();
                CglibProxyFactory cglibProxy = new CglibProxyFactory(userDao);
                //jdk需要提供接口,cglib需要是非私有類,且不能處理final關(guān)鍵字修飾的方法
                Enhancer enhancer = new Enhancer();
                //設(shè)置父類
                enhancer.setSuperclass(UserDao.class);
                //設(shè)置回調(diào)對(duì)象
                enhancer.setCallback(cglibProxy);

                UserDao proxy = (UserDao) enhancer.create();
                proxy.save();

                System.out.println("===1===");

                proxy.delete();

                System.out.println("===2===");

                proxy.saveAndDelete();
            }
        }

        結(jié)果:可以看到方法內(nèi)部調(diào)用沒有走代理

        事務(wù)開始......save
        save
        事務(wù)結(jié)束.......save
        ===1===
        事務(wù)開始......delete
        delete
        事務(wù)結(jié)束.......delete
        ===2===
        事務(wù)開始......saveAndDelete
        save
        delete
        事務(wù)結(jié)束.......saveAndDelete

           簡單理解,invoke方法調(diào)用的對(duì)象沒有增強(qiáng)過(target 模板對(duì)象),invokeSuper方法調(diào)用的對(duì)象已經(jīng)是增強(qiáng)了的(proxy 代理對(duì)象),所以會(huì)再走一遍 MethodInterceptor的 interceptor方法,如果是個(gè)攔截器鏈條,就會(huì)重新在走一次攔截器鏈。

          而且在invoke和invokeSuper中,this指代的對(duì)象不同,

        (1) methodProxy.invokeSuper(o, objects); 中this是代理對(duì)象:aop.UserDao$$EnhancerByCGLIB$$86ae564d@6a1aab78

        (2) methodProxy.invoke(target, objects); 中this指向target對(duì)象:aop.UserDao@eafc191

        補(bǔ)充:Spring的AOP調(diào)用也是用的invoke的套路,我們在AOP中打印this可以看出是目標(biāo)target對(duì)象還是代理proxy對(duì)象。結(jié)果看出是目標(biāo)target對(duì)象

        com.xm.ggn.test.aop.TestServiceImpl@330fff8e

        也可以簡單查看Spring代理:org.springframework.aop.framework.CglibAopProxy.DynamicAdvisedInterceptor#intercept

        @Override
                @Nullable
                public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                    Object oldProxy = null;
                    boolean setProxyContext = false;
                    Object target = null;
                    TargetSource targetSource = this.advised.getTargetSource();
                    try {
                        if (this.advised.exposeProxy) {
                            // Make invocation available if necessary.
                            oldProxy = AopContext.setCurrentProxy(proxy);
                            setProxyContext = true;
                        }
                        // Get as late as possible to minimize the time we "own" the target, in case it comes from a pool...
                        target = targetSource.getTarget();
                        Class<?> targetClass = (target != null ? target.getClass() : null);
                        List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
                        Object retVal;
                        // Check whether we only have one InvokerInterceptor: that is,
                        // no real advice, but just reflective invocation of the target.
                        if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
                            // We can skip creating a MethodInvocation: just invoke the target directly.
                            // Note that the final invoker must be an InvokerInterceptor, so we know
                            // it does nothing but a reflective operation on the target, and no hot
                            // swapping or fancy proxying.
                            Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                            retVal = methodProxy.invoke(target, argsToUse);
                        }
                        else {
                            // We need to create a method invocation...
                            retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
                        }
                        retVal = processReturnType(proxy, target, method, retVal);
                        return retVal;
                    }
                    finally {
                        if (target != null && !targetSource.isStatic()) {
                            targetSource.releaseTarget(target);
                        }
                        if (setProxyContext) {
                            // Restore old proxy.
                            AopContext.setCurrentProxy(oldProxy);
                        }
                    }
                }

        1.4  在Spring的AOP編程中代理的選擇方式:

         

        AOP調(diào)用時(shí)序圖;

          

        查看DefaultAopProxyFactory的源碼:

        //
        // Source code recreated from a .class file by IntelliJ IDEA
        // (powered by Fernflower decompiler)
        //

        package org.springframework.aop.framework;

        import java.io.Serializable;
        import java.lang.reflect.Proxy;
        import org.springframework.aop.SpringProxy;

        public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {
            public DefaultAopProxyFactory() {
            }

            public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
                if (!config.isOptimize() && !config.isProxyTargetClass() && !this.hasNoUserSuppliedProxyInterfaces(config)) {
                    return new JdkDynamicAopProxy(config);
                } else {
                    Class<?> targetClass = config.getTargetClass();
                    if (targetClass == null) {
                        throw new AopConfigException("TargetSource cannot determine target class: Either an interface or a target is required for proxy creation.");
                    } else {
                        return (AopProxy)(!targetClass.isInterface() && !Proxy.isProxyClass(targetClass) ? new ObjenesisCglibAopProxy(config) : new JdkDynamicAopProxy(config));
                    }
                }
            }

            private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {
                Class<?>[] ifcs = config.getProxiedInterfaces();
                return ifcs.length == 0 || ifcs.length == 1 && SpringProxy.class.isAssignableFrom(ifcs[0]);
            }
        }

         

        Spring中強(qiáng)制使用Cglib代理

        <aop:aspectj-autoproxy proxy-target-class="true" />

        SpringBoot中強(qiáng)制使用Cglib代理

        總結(jié):
          如果加入容器的目標(biāo)對(duì)象有實(shí)現(xiàn)接口,用JDK代理
          如果目標(biāo)對(duì)象沒有實(shí)現(xiàn)接口,用Cglib代理   

          如果目標(biāo)對(duì)象實(shí)現(xiàn)了接口,且強(qiáng)制使用cglib代理,則會(huì)使用cglib代理

            參考:http://www.cnblogs.com/qlqwjy/p/8533261.html

         

        補(bǔ)充:查看JDK動(dòng)態(tài)代理和cglib 代理的原理

        Interface1 接口,采用JDK動(dòng)態(tài)代理

        package com.xm.ggn.test.proxy;

        public interface Interface1 {

            void method1();

            void method2(String test);
        }

        CgClass1 類,采用cglib 代理

        package com.xm.ggn.test.proxy;

        public class CgClass1 {

            void method1() {
                System.out.println("com.xm.ggn.test.proxy.CgClass1.method1");
            }

            void method2(String test) {
                System.out.println("com.xm.ggn.test.proxy.CgClass1.method2");
            }
        }

        Client

        package com.xm.ggn.test.proxy;

        import net.sf.cglib.proxy.Enhancer;
        import net.sf.cglib.proxy.MethodInterceptor;
        import net.sf.cglib.proxy.MethodProxy;

        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Method;
        import java.lang.reflect.Proxy;

        public class Client {

            public static void main(String[] args) throws Exception {
                // jdk 動(dòng)態(tài)代理
                Interface1 o = (Interface1) Proxy.newProxyInstance(Interface1.class.getClassLoader(), new Class[]{Interface1.class}, new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("java.lang.reflect.InvocationHandler.invoke " + method + " " + args);
                        return "123";
                    }
                });
                o.method1();
                o.method2("Interface1 method2");

                //****************cglib
                //jdk需要提供接口,cglib需要是非私有類,且不能處理final關(guān)鍵字修飾的方法
                Enhancer enhancer = new Enhancer();
                //設(shè)置父類
                enhancer.setSuperclass(CgClass1.class);
                //設(shè)置回調(diào)對(duì)象
                enhancer.setCallback(new MethodInterceptor() {
                    @Override
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        System.out.println("方法前......" + method.getName());
                        Object o1 = methodProxy.invokeSuper(o, objects);
                        System.out.println("方法后......." + method.getName());
                        return o1;
                    }
                });
                CgClass1 cgClass1 = (CgClass1) enhancer.create();
                cgClass1.method1();
                cgClass1.method2("cg method2");


                Thread.sleep(50 * 10000);
            }
        }

        啟動(dòng)之后控制臺(tái)如下:

        java.lang.reflect.InvocationHandler.invoke public abstract void com.xm.ggn.test.proxy.Interface1.method1() null
        java.lang.reflect.InvocationHandler.invoke public abstract void com.xm.ggn.test.proxy.Interface1.method2(java.lang.String) [Ljava.lang.Object;@5025a98f
        方法前......method1
        com.xm.ggn.test.proxy.CgClass1.method1
        方法后.......method1
        方法前......method2
        com.xm.ggn.test.proxy.CgClass1.method2
        方法后.......method2

        然后采用arthas 進(jìn)行查看

        (1) 查看JDK動(dòng)態(tài)代理

        sc 查看類信息

        [arthas@69520]$ sc *.Interface1
        com.sun.proxy.$Proxy0
        com.xm.ggn.test.proxy.Interface1

        反編譯查看代理類信息

        [arthas@69520]$ jad com.sun.proxy.$Proxy0

        ClassLoader:
        +-sun.misc.Launcher$AppClassLoader@18b4aac2
          +-sun.misc.Launcher$ExtClassLoader@35db61e9

        Location:


        /*
         * Decompiled with CFR.
         *
         * Could not load the following classes:
         *  com.xm.ggn.test.proxy.Interface1
         */
        package com.sun.proxy;

        import com.xm.ggn.test.proxy.Interface1;
        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Method;
        import java.lang.reflect.Proxy;
        import java.lang.reflect.UndeclaredThrowableException;

        public final class $Proxy0
        extends Proxy
        implements Interface1 {
            private static Method m1;
            private static Method m2;
            private static Method m4;
            private static Method m3;
            private static Method m0;

            public $Proxy0(InvocationHandler invocationHandler) {
                super(invocationHandler);
            }

            public final boolean equals(Object object) {
                try {
                    return (Boolean)this.h.invoke(this, m1, new Object[]{object});
                }
                catch (Error | RuntimeException throwable) {
                    throw throwable;
                }
                catch (Throwable throwable) {
                    throw new UndeclaredThrowableException(throwable);
                }
            }

            public final String toString() {
                try {
                    return (String)this.h.invoke(this, m2, null);
                }
                catch (Error | RuntimeException throwable) {
                    throw throwable;
                }
                catch (Throwable throwable) {
                    throw new UndeclaredThrowableException(throwable);
                }
            }

            public final void method2(String string) {
                try {
                    this.h.invoke(this, m4, new Object[]{string});
                    return;
                }
                catch (Error | RuntimeException throwable) {
                    throw throwable;
                }
                catch (Throwable throwable) {
                    throw new UndeclaredThrowableException(throwable);
                }
            }

            public final void method1() {
                try {
                    this.h.invoke(this, m3, null);
                    return;
                }
                catch (Error | RuntimeException throwable) {
                    throw throwable;
                }
                catch (Throwable throwable) {
                    throw new UndeclaredThrowableException(throwable);
                }
            }

            public final int hashCode() {
                try {
                    return (Integer)this.h.invoke(this, m0, null);
                }
                catch (Error | RuntimeException throwable) {
                    throw throwable;
                }
                catch (Throwable throwable) {
                    throw new UndeclaredThrowableException(throwable);
                }
            }

            static {
                try {
                    m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
                    m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
                    m4 = Class.forName("com.xm.ggn.test.proxy.Interface1").getMethod("method2", Class.forName("java.lang.String"));
                    m3 = Class.forName("com.xm.ggn.test.proxy.Interface1").getMethod("method1", new Class[0]);
                    m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
                    return;
                }
                catch (NoSuchMethodException noSuchMethodException) {
                    throw new NoSuchMethodError(noSuchMethodException.getMessage());
                }
                catch (ClassNotFoundException classNotFoundException) {
                    throw new NoClassDefFoundError(classNotFoundException.getMessage());
                }
            }
        }

        可以看到是繼承Proxy 對(duì)象,實(shí)現(xiàn)Interface1 接口。

        1》構(gòu)造方法會(huì)調(diào)用父類的構(gòu)造,IDEA 查看父類構(gòu)造

        java.lang.reflect.Proxy#Proxy(java.lang.reflect.InvocationHandler)

        protected InvocationHandler h;

            protected Proxy(InvocationHandler h) {
                Objects.requireNonNull(h);
                this.h = h;
            }

        2》 查看method1、method2 方法,實(shí)際是反射調(diào)用InvocationHandler, 也就是我們上面匿名類創(chuàng)建的對(duì)象(所以這個(gè)匿名類一般會(huì)根據(jù)方法名稱判斷是toString、hashcode方法進(jìn)行特殊處理)

        public final void method2(String string) {
                try {
                    this.h.invoke(this, m4, new Object[]{string});
                    return;
                }
                catch (Error | RuntimeException throwable) {
                    throw throwable;
                }
                catch (Throwable throwable) {
                    throw new UndeclaredThrowableException(throwable);
                }
            }

            public final void method1() {
                try {
                    this.h.invoke(this, m3, null);
                    return;
                }
                catch (Error | RuntimeException throwable) {
                    throw throwable;
                }
                catch (Throwable throwable) {
                    throw new UndeclaredThrowableException(throwable);
                }
            }

        (2) 查看Cglib 代理

        1》搜索類

        [arthas@104968]$ sc *.CgClass1
        com.xm.ggn.test.proxy.CgClass1
        com.xm.ggn.test.proxy.CgClass1$$EnhancerByCGLIB$$f752d1b3

        2》反編譯查看類:可以看到主要是繼承代理類,實(shí)現(xiàn)cglib 的類,核心是通過MethodInteceptor 進(jìn)行處理的

        [arthas@104968]$ jad com.xm.ggn.test.proxy.CgClass1$$EnhancerByCGLIB$$f752d1b3

        ClassLoader:
        +-sun.misc.Launcher$AppClassLoader@18b4aac2
          +-sun.misc.Launcher$ExtClassLoader@1f256477

        Location:
        /E:/xiangmu/bs-media-gitee/bs-media/media-server/target/classes/

        /*
         * Decompiled with CFR.
         *
         * Could not load the following classes:
         *  com.xm.ggn.test.proxy.CgClass1
         */
        package com.xm.ggn.test.proxy;

        import com.xm.ggn.test.proxy.CgClass1;
        import java.lang.reflect.Method;
        import net.sf.cglib.core.ReflectUtils;
        import net.sf.cglib.core.Signature;
        import net.sf.cglib.proxy.Callback;
        import net.sf.cglib.proxy.Factory;
        import net.sf.cglib.proxy.MethodInterceptor;
        import net.sf.cglib.proxy.MethodProxy;

        public class CgClass1$$EnhancerByCGLIB$$f752d1b3
        extends CgClass1
        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$method1$0$Method;
            private static final MethodProxy CGLIB$method1$0$Proxy;
            private static final Object[] CGLIB$emptyArgs;
            private static final Method CGLIB$method2$1$Method;
            private static final MethodProxy CGLIB$method2$1$Proxy;
            private static final Method CGLIB$equals$2$Method;
            private static final MethodProxy CGLIB$equals$2$Proxy;
            private static final Method CGLIB$toString$3$Method;
            private static final MethodProxy CGLIB$toString$3$Proxy;
            private static final Method CGLIB$hashCode$4$Method;
            private static final MethodProxy CGLIB$hashCode$4$Proxy;
            private static final Method CGLIB$clone$5$Method;
            private static final MethodProxy CGLIB$clone$5$Proxy;

            static void CGLIB$STATICHOOK1() {
                CGLIB$THREAD_CALLBACKS = new ThreadLocal();
                CGLIB$emptyArgs = new Object[0];
                Class<?> clazz = Class.forName("com.xm.ggn.test.proxy.CgClass1$$EnhancerByCGLIB$$f752d1b3");
                Class<?> clazz2 = Class.forName("java.lang.Object");
                Method[] methodArray = ReflectUtils.findMethods(new String[]{"equals""(Ljava/lang/Object;)Z""toString""()Ljava/lang/String;""hashCode""()I""clone""()Ljava/lang/Object;"}, clazz2.getDeclaredMethods());
                CGLIB$equals$2$Method = methodArray[0];
                CGLIB$equals$2$Proxy = MethodProxy.create(clazz2, clazz, "(Ljava/lang/Object;)Z""equals""CGLIB$equals$2");
                CGLIB$toString$3$Method = methodArray[1];
                CGLIB$toString$3$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/String;""toString""CGLIB$toString$3");
                CGLIB$hashCode$4$Method = methodArray[2];
                CGLIB$hashCode$4$Proxy = MethodProxy.create(clazz2, clazz, "()I""hashCode""CGLIB$hashCode$4");
                CGLIB$clone$5$Method = methodArray[3];
                CGLIB$clone$5$Proxy = MethodProxy.create(clazz2, clazz, "()Ljava/lang/Object;""clone""CGLIB$clone$5");
                clazz2 = Class.forName("com.xm.ggn.test.proxy.CgClass1");
                Method[] methodArray2 = ReflectUtils.findMethods(new String[]{"method1""()V""method2""(Ljava/lang/String;)V"}, clazz2.getDeclaredMethods());
                CGLIB$method1$0$Method = methodArray2[0];
                CGLIB$method1$0$Proxy = MethodProxy.create(clazz2, clazz, "()V""method1""CGLIB$method1$0");
                CGLIB$method2$1$Method = methodArray2[1];
                CGLIB$method2$1$Proxy = MethodProxy.create(clazz2, clazz, "(Ljava/lang/String;)V""method2""CGLIB$method2$1");
            }

            final void CGLIB$method1$0() {
                super.method1();
            }

            final void method1() {
                MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
                if (methodInterceptor == null) {
                    CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$BIND_CALLBACKS(this);
                    methodInterceptor = this.CGLIB$CALLBACK_0;
                }
                if (methodInterceptor != null) {
                    Object object = methodInterceptor.intercept(this, CGLIB$method1$0$Method, CGLIB$emptyArgs, CGLIB$method1$0$Proxy);
                    return;
                }
                super.method1();
            }

            final void CGLIB$method2$1(String string) {
                super.method2(string);
            }

            final void method2(String string) {
                MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
                if (methodInterceptor == null) {
                    CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$BIND_CALLBACKS(this);
                    methodInterceptor = this.CGLIB$CALLBACK_0;
                }
                if (methodInterceptor != null) {
                    Object object = methodInterceptor.intercept(this, CGLIB$method2$1$Method, new Object[]{string}, CGLIB$method2$1$Proxy);
                    return;
                }
                super.method2(string);
            }

            final boolean CGLIB$equals$2(Object object) {
                return super.equals(object);
            }

            public final boolean equals(Object object) {
                MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
                if (methodInterceptor == null) {
                    CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$BIND_CALLBACKS(this);
                    methodInterceptor = this.CGLIB$CALLBACK_0;
                }
                if (methodInterceptor != null) {
                    Object object2 = methodInterceptor.intercept(this, CGLIB$equals$2$Method, new Object[]{object}, CGLIB$equals$2$Proxy);
                    return object2 == null ? false : (Boolean)object2;
                }
                return super.equals(object);
            }

            final String CGLIB$toString$3() {
                return super.toString();
            }

            public final String toString() {
                MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
                if (methodInterceptor == null) {
                    CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$BIND_CALLBACKS(this);
                    methodInterceptor = this.CGLIB$CALLBACK_0;
                }
                if (methodInterceptor != null) {
                    return (String)methodInterceptor.intercept(this, CGLIB$toString$3$Method, CGLIB$emptyArgs, CGLIB$toString$3$Proxy);
                }
                return super.toString();
            }

            final int CGLIB$hashCode$4() {
                return super.hashCode();
            }

            public final int hashCode() {
                MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
                if (methodInterceptor == null) {
                    CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$BIND_CALLBACKS(this);
                    methodInterceptor = this.CGLIB$CALLBACK_0;
                }
                if (methodInterceptor != null) {
                    Object object = methodInterceptor.intercept(this, CGLIB$hashCode$4$Method, CGLIB$emptyArgs, CGLIB$hashCode$4$Proxy);
                    return object == null ? 0 : ((Number)object).intValue();
                }
                return super.hashCode();
            }

            final Object CGLIB$clone$5() throws CloneNotSupportedException {
                return super.clone();
            }

            protected final Object clone() throws CloneNotSupportedException {
                MethodInterceptor methodInterceptor = this.CGLIB$CALLBACK_0;
                if (methodInterceptor == null) {
                    CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$BIND_CALLBACKS(this);
                    methodInterceptor = this.CGLIB$CALLBACK_0;
                }
                if (methodInterceptor != null) {
                    return methodInterceptor.intercept(this, CGLIB$clone$5$Method, CGLIB$emptyArgs, CGLIB$clone$5$Proxy);
                }
                return super.clone();
            }

            public static MethodProxy CGLIB$findMethodProxy(Signature signature) {
                String string = ((Object)signature).toString();
                switch (string.hashCode()) {
                    case -724084219: {
                        if (!string.equals("method1()V")) break;
                        return CGLIB$method1$0$Proxy;
                    }
                    case -518805714: {
                        if (!string.equals("method2(Ljava/lang/String;)V")) break;
                        return CGLIB$method2$1$Proxy;
                    }
                    case -508378822: {
                        if (!string.equals("clone()Ljava/lang/Object;")) break;
                        return CGLIB$clone$5$Proxy;
                    }
                    case 1826985398: {
                        if (!string.equals("equals(Ljava/lang/Object;)Z")) break;
                        return CGLIB$equals$2$Proxy;
                    }
                    case 1913648695: {
                        if (!string.equals("toString()Ljava/lang/String;")) break;
                        return CGLIB$toString$3$Proxy;
                    }
                    case 1984935277: {
                        if (!string.equals("hashCode()I")) break;
                        return CGLIB$hashCode$4$Proxy;
                    }
                }
                return null;
            }

            public CgClass1$$EnhancerByCGLIB$$f752d1b3() {
                CgClass1$$EnhancerByCGLIB$$f752d1b3 cgClass1$$EnhancerByCGLIB$$f752d1b3 = this;
                CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$BIND_CALLBACKS(cgClass1$$EnhancerByCGLIB$$f752d1b3);
            }

            public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] callbackArray) {
                CGLIB$THREAD_CALLBACKS.set(callbackArray);
            }

            public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] callbackArray) {
                CGLIB$STATIC_CALLBACKS = callbackArray;
            }

            private static final void CGLIB$BIND_CALLBACKS(Object object) {
                block2: {
                    Object object2;
                    block3: {
                        CgClass1$$EnhancerByCGLIB$$f752d1b3 cgClass1$$EnhancerByCGLIB$$f752d1b3 = (CgClass1$$EnhancerByCGLIB$$f752d1b3)object;
                        if (cgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$BOUNDbreak block2;
                        cgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$BOUND = true;
                        object2 = CGLIB$THREAD_CALLBACKS.get();
                        if (object2 != null) break block3;
                        object2 = CGLIB$STATIC_CALLBACKS;
                        if (CGLIB$STATIC_CALLBACKS == null) break block2;
                    }
                    cgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])object2)[0];
                }
            }

            public Object newInstance(Callback[] callbackArray) {
                CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$SET_THREAD_CALLBACKS(callbackArray);
                CgClass1$$EnhancerByCGLIB$$f752d1b3 cgClass1$$EnhancerByCGLIB$$f752d1b3 = new CgClass1$$EnhancerByCGLIB$$f752d1b3();
                CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$SET_THREAD_CALLBACKS(null);
                return cgClass1$$EnhancerByCGLIB$$f752d1b3;
            }

            public Object newInstance(Callback callback) {
                CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$SET_THREAD_CALLBACKS(new Callback[]{callback});
                CgClass1$$EnhancerByCGLIB$$f752d1b3 cgClass1$$EnhancerByCGLIB$$f752d1b3 = new CgClass1$$EnhancerByCGLIB$$f752d1b3();
                CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$SET_THREAD_CALLBACKS(null);
                return cgClass1$$EnhancerByCGLIB$$f752d1b3;
            }

            public Object newInstance(Class[] classArray, Object[] objectArray, Callback[] callbackArray) {
                CgClass1$$EnhancerByCGLIB$$f752d1b3 cgClass1$$EnhancerByCGLIB$$f752d1b3;
                CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$SET_THREAD_CALLBACKS(callbackArray);
                Class[] classArray2 = classArray;
                switch (classArray.length) {
                    case 0: {
                        cgClass1$$EnhancerByCGLIB$$f752d1b3 = new CgClass1$$EnhancerByCGLIB$$f752d1b3();
                        break;
                    }
                    default: {
                        throw new IllegalArgumentException("Constructor not found");
                    }
                }
                CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$SET_THREAD_CALLBACKS(null);
                return cgClass1$$EnhancerByCGLIB$$f752d1b3;
            }

            public Callback getCallback(int n) {
                MethodInterceptor methodInterceptor;
                CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$BIND_CALLBACKS(this);
                switch (n) {
                    case 0: {
                        methodInterceptor = this.CGLIB$CALLBACK_0;
                        break;
                    }
                    default: {
                        methodInterceptor = null;
                    }
                }
                return methodInterceptor;
            }

            public void setCallback(int n, Callback callback) {
                switch (n) {
                    case 0: {
                        this.CGLIB$CALLBACK_0 = (MethodInterceptor)callback;
                        break;
                    }
                }
            }

            public Callback[] getCallbacks() {
                CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$BIND_CALLBACKS(this);
                CgClass1$$EnhancerByCGLIB$$f752d1b3 cgClass1$$EnhancerByCGLIB$$f752d1b3 = this;
                return new Callback[]{this.CGLIB$CALLBACK_0};
            }

            public void setCallbacks(Callback[] callbackArray) {
                Callback[] callbackArray2 = callbackArray;
                CgClass1$$EnhancerByCGLIB$$f752d1b3 cgClass1$$EnhancerByCGLIB$$f752d1b3 = this;
                this.CGLIB$CALLBACK_0 = (MethodInterceptor)callbackArray[0];
            }

            static {
                CgClass1$$EnhancerByCGLIB$$f752d1b3.CGLIB$STATICHOOK1();
            }
        }


          作者 |  QiaoZhi

        來源 |  cnblogs.com/qlqwjy/p/7550609.html



        加鋒哥微信: java3459  
        圍觀鋒哥朋友圈,天天推送Java干貨!

        瀏覽 51
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            天天操,夜夜操 | 综合色片 | 被老师按在教室作爱 | 日本护士献身取精xxxxx | 韩国毛片在线观看 | sao货水真多把你cao烂69视频 | 我把女的日出白液视频 | 中国老女人色色 | 久久精品国产成人 | 张柏芝亚洲一区二区三区 |