代理模式

代理模式說明與特點

代理模式給某一個對象提供一個代理對象岛琼,并由代理對象控制對原對象的引用床三。

在有些情況下酥泛,一個客戶不能或者不想直接訪問另一個對象,這時需要找一個中介幫忙完成某項任務(wù)莉测,這個中介就是代理對象颜骤。例如,購買火車票不一定要去火車站買捣卤,可以通過 12306 網(wǎng)站或者去火車票代售點買忍抽。又如找女朋友、找保姆董朝、找工作等都可以通過找中介完成鸠项。

代理模式結(jié)構(gòu)

  1. 類圖


    proxy
  2. 對應(yīng)角色
  • 抽象對象角色(AbstractObject): 聲明了目標(biāo)對象和代理對象的共同接口。
  • 目標(biāo)對象角色(RealObject): 定義了代理對象所代表的目標(biāo)對象子姜。
  • 代理對象角色(ProxyObject): 代理對象內(nèi)部含有目標(biāo)對象的引用祟绊,從而可以在任何時候操作目標(biāo)對象;

代碼演示

代理模式又分為靜態(tài)代理和動態(tài)代理 哥捕,首先我們先看一下靜態(tài)代理的實現(xiàn)牧抽。

靜態(tài)代理演示
  1. 抽象對象角色
/**
 * 抽象對象角色
 *
 * @author hui.wang
 * @since 30 November 2018
 */
public interface Moveable {

    void move();
}
  1. 目標(biāo)對象角色
/**
 * 目標(biāo)對象角色
 *
 * @author hui.wang
 * @since 30 November 2018
 */
public class Tank implements Moveable {

    @Override
    public void move() {
        System.out.println("moving......");
    }
}
  1. 代理對象角色
/**
 * 代理對象角色,使用聚合實現(xiàn)
 *
 * @author hui.wang
 * @since 30 November 2018
 */
public class TankTwo implements Moveable{

    private Moveable moveable;

    public TankTwo(Moveable moveable) {
        this.moveable = moveable;
    }

    @Override
    public void move() {
        System.out.println("聚合實現(xiàn)");
        moveable.move();
        System.out.println("聚合實現(xiàn)");
    }
}

測試

        //目標(biāo)對象
        Tank target = new Tank();

        //代理對象
        TankTwo proxy = new TankTwo(target);
        proxy.move();

輸出

聚合實現(xiàn)
moving......
聚合實現(xiàn)
  1. 靜態(tài)代理代碼比較簡單扭弧,使用簡單的聚合方式,將目標(biāo)對象聚合到代理對象中记舆。
  2. 缺點:因為代理對象需要與目標(biāo)對象實現(xiàn)一樣的接口鸽捻,所以會有很多代理類,類太多泽腮。同時御蒲,一旦接口增加方法,目標(biāo)對象與代理對象都要維護(hù)。耦合太強
動態(tài)代理

接下來我們看一下動態(tài)代理代碼诊赊,這里主要演示一下JDK自帶的動態(tài)代理和CgLib的動態(tài)代理

JDK動態(tài)代理

使用JDK動態(tài)代理一共分三步:

  1. 定義一個抽象接口(抽象對象
  2. 實現(xiàn)這個抽象接口(目標(biāo)對象
  3. 定義一個代理對象并實現(xiàn)java.lang.reflect.InvocationHandler接口(代理對象

代碼如下:

/**
 *  抽象接口
 *
 * @author hui.wang
 * @since 14 May 2019
 */
public interface Service {

    void say();
}
/**
 * 目標(biāo)對象
 *
 * @author hui.wang
 * @since 14 May 2019
 */
public class ServiceImpl implements Service {

    @Override
    public void say() {
        System.out.println("hello world");
    }
}
/**
 * 代理對象厚满,實現(xiàn) {@link InvocationHandler}接口
 *
 * @author hui.wang
 * @since 14 May 2019
 */
public class ServiceProxy implements InvocationHandler {

    /**
     * 代理的目標(biāo)對象
     */
    private Object target;

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

    /**
     * 獲取代理對象
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before ....");
        Object result = method.invoke(target, args);
        System.out.println("after ...");
        return result;
    }
}

測試

        /**
         * 設(shè)置 sun.misc.ProxyGenerator.saveGeneratedFiles 為true,將生成的代理保存
         */
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        Service service = new ServiceProxy(new ServiceImpl()).getProxy();
        service.say();

打印結(jié)果:

before ....
hello world
after ...

使用JDK自帶代理已經(jīng)演示完了碧磅,這你需要注意碘箍,在測試的時候我把sun.misc.ProxyGenerator.saveGeneratedFiles屬性設(shè)置了true了遵馆,運行完程序,會將生成的代理對象的class文件保存到你本地磁盤丰榴。

CgLib動態(tài)代理

CgLib動態(tài)代理可以不用抽象出抽象對象角色货邓,可以直接生成目標(biāo)對象的代理類。
代碼如下:

/**
 * 目標(biāo)對象角色
 *
 * @author hui.wang
 * @since 30 November 2018
 */
public class Tank{

    public void move() {
        System.out.println("moving......");
    }
}
/**
 * 動態(tài)代理對象四濒, 實現(xiàn) {@link MethodInterceptor}接口
 *
 * @author hui.wang
 * @since 30 November 2018
 */
public class TankProxy implements MethodInterceptor{

    /**
     * 目標(biāo)對象
     */
    private Object target;

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

    /**
     * 獲取代理對象
     */
    public Object getProxyInstance() {
        //1.工具類
        Enhancer enhancer = new Enhancer();
        //2.設(shè)置父類
        enhancer.setSuperclass(target.getClass());
        //3.設(shè)置回調(diào)函數(shù)
        enhancer.setCallback(this);
        //4.創(chuàng)建子類(代理對象)
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib proxy start.......");
        //執(zhí)行目標(biāo)對象的方法
        Object value = method.invoke(target, objects);
        System.out.println("cglib proxy end.......");
        return value;
    }
}

測試

        Tank target = new Tank();
        Tank proxy = (Tank) new TankProxy(target).getProxyInstance();
        proxy.move();

打印結(jié)果:

cglib proxy start.......
moving......
cglib proxy end.......

講到這里動態(tài)代理的實現(xiàn)基本上是講完了换况。接下來我們看一下JDKCgLib 動態(tài)的實現(xiàn)原理。

JDK動態(tài)代理實現(xiàn)原理

繼續(xù)上面JDK動態(tài)代理的代碼盗蟆,我們分別定義了三個類戈二,分別是

  1. 定義一個抽象接口(抽象對象):Service
  2. 實現(xiàn)這個抽象接口(目標(biāo)對象): ServiceImpl
  3. 定義一個代理對象并實現(xiàn):(代理對象) :ServiceProxy

測試的時候,我們設(shè)置了System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")喳资,然后我們發(fā)現(xiàn)目錄里面多了一個$Proxy0觉吭,反編譯后代碼如下:

package com.sun.proxy;

import com.hui.wang.jdk.learn.proxy.Service;
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 Service {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

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

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void say() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.hui.wang.jdk.learn.proxy.Service").getMethod("say");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

OK ,看到這段代碼骨饿,估計你已經(jīng)知道JDK的實現(xiàn)了亏栈。JDK幫我們生成了一個$Proxy0類,然后這個類代理了目標(biāo)對象宏赘。下面我們先看一下JDK動態(tài)代理調(diào)用過程绒北。

測試的時候,我們調(diào)用了這段代碼

        Service service = new ServiceProxy(new ServiceImpl()).getProxy();
        service.say();

這里調(diào)用say()方法就是調(diào)用生成的代理類$Proxy0say()方法:

    public final void say() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

這里會調(diào)用super.h.invoke(this, m3, (Object[])null);這里的super.h是父類的成員變量察署,這個變量的賦值在代理類$Proxy0創(chuàng)建的時候賦值的

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

這里的var1值就是我們的自己創(chuàng)建的代理類ServiceProxy自己:

       /**
     * 獲取代理對象
     */
    public <T> T getProxy() {
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

繼續(xù)回到super.h.invoke(this, m3, (Object[])null);這句闷游,其中super.h就是ServiceProxy實例,因此super.h.invoke(this, m3, (Object[])null);執(zhí)行的就是:

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before ....");
        Object result = method.invoke(target, args);
        System.out.println("after ...");
        return result;
    }

所以整個代理過程就很清楚了贴汪。

接下來我們看一下細(xì)節(jié)實現(xiàn)脐往。我們先從ServiceProxy這個類開始。動態(tài)代理對象的生成是在return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);這句話上面扳埂。我們點擊去看一下业簿。

 /**
     * @param loader 類加載器
     * @param interfaces 目標(biāo)對象實現(xiàn)的接口
     * @param h InvocationHandler 實現(xiàn)類
     */
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
            Class<?>[] interfaces,
            InvocationHandler h)
            throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        // 生成代理類 class 對象
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            
            // 獲取代理類的構(gòu)造方法
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        // 設(shè)置 accessible
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            // 通過反射創(chuàng)建代理類型的實例
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

代碼也比較簡單,先生成代理對象的class阳懂,然后用反射創(chuàng)建代理對象的實例梅尤。生成代理對象的class 對象主要在getProxyClass0方法里面,我們進(jìn)去看看岩调。

/**
     * 生成代理對象的class對象
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
            Class<?>... interfaces) {
        
        // 目標(biāo)對象實現(xiàn)的接口不能超過 65535 個
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

這里邏輯很簡單巷燥,所有的邏輯都在proxyClassCache.get(loader, interfaces);里面。這里使用了WeakCache緩存号枕,當(dāng)WeakCache中沒有緩存相應(yīng)接口的代理類缰揪,則會調(diào)用ProxyClassFactory類的apply方法來創(chuàng)建代理類。我們看一下:

/**
     * A factory function that generates, defines and returns the proxy class given
     * the ClassLoader and array of interfaces.
     */
    private static final class ProxyClassFactory
            implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // 代理類名字的前綴
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        // 生成代理類的計數(shù)器
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);

            // 接口的校驗
            for (Class<?> intf : interfaces) {

                /**
                 * 校驗類加載器是否能通過接口名稱加載該類
                 */
                Class<?> interfaceClass = null;
                try {
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }

                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                            intf + " is not visible from class loader");
                }

                /**
                 * 校驗該類是否是接口類型
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                            interfaceClass.getName() + " is not an interface");
                }

                /**
                 * 校驗接口是否重復(fù)
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                            "repeated interface: " + interfaceClass.getName());
                }
            }

            // 代理包名
            String proxyPkg = null;
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /**
             * 非public接口葱淳,代理類的包名與接口的包名相同
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                                "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // public代理接口钝腺,使用com.sun.proxy包名
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /**
             * 生成代理類的名字
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * 生成的代理類的字節(jié)碼文件
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces, accessFlags);
            try {
                // 使用類加載器將代理類的字節(jié)碼文件加載到JVM中
                return defineClass0(loader, proxyName,
                        proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

到這里基本上講完了JDK動態(tài)代理的過程了抛姑。

CgLib動態(tài)代理實現(xiàn)原理

這里我們重新過一下代碼

  1. 目標(biāo)對象代碼:

/**
 * 目標(biāo)對象角色
 *
 * @author hui.wang
 * @since 30 November 2018
 */
public class Tank{

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

    /**
     * 這里我加了一個 final 方法
     */
    public final void finalMethod() {
        System.out.println("final method....");
    }
}
  1. 代理對象:
/**
 * 動態(tài)代理對象, 實現(xiàn) {@link MethodInterceptor}接口
 *
 * @author hui.wang
 * @since 30 November 2018
 */
public class TankProxy implements MethodInterceptor {

    /**
     * 目標(biāo)對象
     */
    private Object target;

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

    /**
     * 獲取代理對象
     */
    public Object getProxyInstance() {
        //1.工具類
        Enhancer enhancer = new Enhancer();
        //2.設(shè)置父類
        enhancer.setSuperclass(target.getClass());
        //3.設(shè)置回調(diào)函數(shù)
        enhancer.setCallback(this);
        //4.創(chuàng)建子類(代理對象)
        return enhancer.create();
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib proxy start.......");
        //執(zhí)行目標(biāo)對象的方法
        Object value = method.invoke(target, objects);
        System.out.println("cglib proxy end.......");
        return value;
    }
}

代理對象實現(xiàn)了MethodInterceptor接口拍屑。

  1. 測試類
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/learn/design-pattern");
        Tank target = new Tank();
        Tank proxy = (Tank) new TankProxy(target).getProxyInstance();
        proxy.move();
        proxy.finalMethod();

System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "/Users/learn/design-pattern");這句話是將CgLib產(chǎn)生的代理對象保存到本地磁盤途戒。

  1. 打印結(jié)果:
cglib proxy start.......
moving......
cglib proxy end.......
final method....

這里可以看到final修飾的方法沒有被代理。
本地磁盤會保存一個class文件僵驰,反編譯代碼如下:

public class Tank$$EnhancerByCGLIB$$46c52377 extends Tank 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$move$0$Method;
    private static final MethodProxy CGLIB$move$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$equals$1$Method;
    private static final MethodProxy CGLIB$equals$1$Proxy;
    private static final Method CGLIB$toString$2$Method;
    private static final MethodProxy CGLIB$toString$2$Proxy;
    private static final Method CGLIB$hashCode$3$Method;
    private static final MethodProxy CGLIB$hashCode$3$Proxy;
    private static final Method CGLIB$clone$4$Method;
    private static final MethodProxy CGLIB$clone$4$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.hui.wang.design.pattern.v3.Tank$$EnhancerByCGLIB$$46c52377");
        Class var1;
        CGLIB$move$0$Method = ReflectUtils.findMethods(new String[]{"move", "()V"}, (var1 = Class.forName("com.hui.wang.design.pattern.v3.Tank")).getDeclaredMethods())[0];
        CGLIB$move$0$Proxy = MethodProxy.create(var1, var0, "()V", "move", "CGLIB$move$0");
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$1$Method = var10000[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        CGLIB$toString$2$Method = var10000[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
        CGLIB$hashCode$3$Method = var10000[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
        CGLIB$clone$4$Method = var10000[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
    }

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

    public final void move() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$move$0$Method, CGLIB$emptyArgs, CGLIB$move$0$Proxy);
        } else {
            super.move();
        }
    }

    final boolean CGLIB$equals$1(Object var1) {
        return super.equals(var1);
    }

    public final boolean equals(Object var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            Object var2 = var10000.intercept(this, CGLIB$equals$1$Method, new Object[]{var1}, CGLIB$equals$1$Proxy);
            return var2 == null ? false : (Boolean)var2;
        } else {
            return super.equals(var1);
        }
    }

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

    public final String toString() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy) : super.toString();
    }

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

    public final int hashCode() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            Object var1 = var10000.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
            return var1 == null ? 0 : ((Number)var1).intValue();
        } else {
            return super.hashCode();
        }
    }

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

    protected final Object clone() throws CloneNotSupportedException {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? var10000.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy) : super.clone();
    }

    public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
        String var10000 = var0.toString();
        switch(var10000.hashCode()) {
        case -508378822:
            if (var10000.equals("clone()Ljava/lang/Object;")) {
                return CGLIB$clone$4$Proxy;
            }
            break;
        case 1243513348:
            if (var10000.equals("move()V")) {
                return CGLIB$move$0$Proxy;
            }
            break;
        case 1826985398:
            if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                return CGLIB$equals$1$Proxy;
            }
            break;
        case 1913648695:
            if (var10000.equals("toString()Ljava/lang/String;")) {
                return CGLIB$toString$2$Proxy;
            }
            break;
        case 1984935277:
            if (var10000.equals("hashCode()I")) {
                return CGLIB$hashCode$3$Proxy;
            }
        }

        return null;
    }

    public Tank$$EnhancerByCGLIB$$46c52377() {
        CGLIB$BIND_CALLBACKS(this);
    }

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

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

    private static final void CGLIB$BIND_CALLBACKS(Object var0) {
        Tank$$EnhancerByCGLIB$$46c52377 var1 = (Tank$$EnhancerByCGLIB$$46c52377)var0;
        if (!var1.CGLIB$BOUND) {
            var1.CGLIB$BOUND = true;
            Object var10000 = CGLIB$THREAD_CALLBACKS.get();
            if (var10000 == null) {
                var10000 = CGLIB$STATIC_CALLBACKS;
                if (CGLIB$STATIC_CALLBACKS == null) {
                    return;
                }
            }

            var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
        }

    }

    public Object newInstance(Callback[] var1) {
        CGLIB$SET_THREAD_CALLBACKS(var1);
        Tank$$EnhancerByCGLIB$$46c52377 var10000 = new Tank$$EnhancerByCGLIB$$46c52377();
        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Callback var1) {
        CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
        Tank$$EnhancerByCGLIB$$46c52377 var10000 = new Tank$$EnhancerByCGLIB$$46c52377();
        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
        CGLIB$SET_THREAD_CALLBACKS(var3);
        Tank$$EnhancerByCGLIB$$46c52377 var10000 = new Tank$$EnhancerByCGLIB$$46c52377;
        switch(var1.length) {
        case 0:
            var10000.<init>();
            CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
            return var10000;
        default:
            throw new IllegalArgumentException("Constructor not found");
        }
    }

    public Callback getCallback(int var1) {
        CGLIB$BIND_CALLBACKS(this);
        MethodInterceptor var10000;
        switch(var1) {
        case 0:
            var10000 = this.CGLIB$CALLBACK_0;
            break;
        default:
            var10000 = null;
        }

        return var10000;
    }

    public void setCallback(int var1, Callback var2) {
        switch(var1) {
        case 0:
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
        default:
        }
    }

    public Callback[] getCallbacks() {
        CGLIB$BIND_CALLBACKS(this);
        return new Callback[]{this.CGLIB$CALLBACK_0};
    }

    public void setCallbacks(Callback[] var1) {
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
    }

    static {
        CGLIB$STATICHOOK1();
    }
}

代碼較長喷斋,我們簡單說一下∷廛睿看這段代碼星爪,我們可以知道

  1. CgLib是使用繼承的方式實現(xiàn)的代理,和JDK不一樣粉私,不需要抽象出來interface顽腾。
  2. 如果方法是 final 類型,將不會被代碼诺核。

接下來我們分析一下CgLib生成出來的這個類抄肖。

    // 靜態(tài)代碼塊,首先會被執(zhí)行
    static {
        CGLIB$STATICHOOK1();
    }
    
    // 被靜態(tài)代碼塊調(diào)用
    static void CGLIB$STATICHOOK1() {
        // 存放 ThreadLocal
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        // 空參數(shù)
        CGLIB$emptyArgs = new Object[0];
        // 代理的 class 對象
        Class var0 = Class.forName("com.hui.wang.design.pattern.v3.Tank$$EnhancerByCGLIB$$46c52377");
        // 被代理的類 (var1 = Class.forName("com.hui.wang.design.pattern.v3.Tank"))
        Class var1;
        // 父類原始方法
        CGLIB$move$0$Method = ReflectUtils.findMethods(new String[]{"move", "()V"}, (var1 = Class.forName("com.hui.wang.design.pattern.v3.Tank")).getDeclaredMethods())[0];
        // 被代理的父類方法
        CGLIB$move$0$Proxy = MethodProxy.create(var1, var0, "()V", "move", "CGLIB$move$0");
        // Object 的原始方法集合
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        // equals 原始方法
        CGLIB$equals$1$Method = var10000[0];
        // toString 代理方法
        CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        // toString 原始方法
        CGLIB$toString$2$Method = var10000[1];
        //  toString 代理方法
        CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
        // hashCode 原始方法
        CGLIB$hashCode$3$Method = var10000[2];
        // hashCode 代理方法
        CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
        // clone 原始方法
        CGLIB$clone$4$Method = var10000[3];
        // clone 代理方法
        CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
    }

// =================== 變量窖杀,在上個方法中賦值========== //
    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$move$0$Method;
    private static final MethodProxy CGLIB$move$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$equals$1$Method;
    private static final MethodProxy CGLIB$equals$1$Proxy;
    private static final Method CGLIB$toString$2$Method;
    private static final MethodProxy CGLIB$toString$2$Proxy;
    private static final Method CGLIB$hashCode$3$Method;
    private static final MethodProxy CGLIB$hashCode$3$Proxy;
    private static final Method CGLIB$clone$4$Method;
    private static final MethodProxy CGLIB$clone$4$Proxy;

我們通過代理類的源碼可以看到漓摩,代理類會獲得所有在父類繼承來的方法,并且會有MethodProxy與之對應(yīng)入客,比如CGLIB$equals$1$Method管毙、CGLIB$equals$1$Proxy;

接下來我們看一下proxy.move();方法的調(diào)用:

    public final void move() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (this.CGLIB$CALLBACK_0 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$move$0$Method, CGLIB$emptyArgs, CGLIB$move$0$Proxy);
        } else {
            super.move();
        }
    }

這里就調(diào)用MethodInterceptorintercept方法,這里的MethodInterceptor就是TankProxy對象桌硫,所以就調(diào)用到了

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib proxy start.......");
        //執(zhí)行目標(biāo)對象的方法
        Object value = method.invoke(target, objects);
        System.out.println("cglib proxy end.......");
        return value;
    }

這就是整個調(diào)用過程夭咬,需要說明一下methodProxy就是父類的原始方法。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末铆隘,一起剝皮案震驚了整個濱河市卓舵,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌膀钠,老刑警劉巖掏湾,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異托修,居然都是意外死亡忘巧,警方通過查閱死者的電腦和手機恒界,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門睦刃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人十酣,你說我怎么就攤上這事涩拙〖食ぃ” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵兴泥,是天一觀的道長工育。 經(jīng)常有香客問我,道長搓彻,這世上最難降的妖魔是什么如绸? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮旭贬,結(jié)果婚禮上怔接,老公的妹妹穿的比我還像新娘。我一直安慰自己稀轨,他們只是感情好扼脐,可當(dāng)我...
    茶點故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著奋刽,像睡著了一般瓦侮。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上佣谐,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天肚吏,我揣著相機與錄音,去河邊找鬼台谍。 笑死须喂,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的趁蕊。 我是一名探鬼主播坞生,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼掷伙!你這毒婦竟也來了是己?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤任柜,失蹤者是張志新(化名)和其女友劉穎卒废,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宙地,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡摔认,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了宅粥。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片参袱。...
    茶點故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出抹蚀,到底是詐尸還是另有隱情剿牺,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布环壤,位于F島的核電站晒来,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏郑现。R本人自食惡果不足惜湃崩,卻給世界環(huán)境...
    茶點故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望接箫。 院中可真熱鬧竹习,春花似錦、人聲如沸列牺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瞎领。三九已至泌辫,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間九默,已是汗流浹背震放。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留驼修,地道東北人殿遂。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像乙各,于是被迫代替她去往敵國和親墨礁。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,792評論 2 345