代理模式詳解

什么是代理模式


代理模式是對象的結構模式。代理模式為其他對象提供一種代理以控制對這個對象的訪問。
簡單來說,在某些情況下,一個客戶不想或者不能直接引用另一個對象,而代理對象可以在客戶端和目標對象之間起到中介的作用。

舉個例子
我們打算結婚,但是婚禮的細節我們不想管,這時候我們找到婚慶公司,讓他們幫我們包攬婚禮的細節,這就是“代理模式”。既然是代理模式,那么就應該有一個代理角色和真實角色。例子中的“我們”就是真實角色,“婚慶公司”就是代理角色。
我們打算結婚而不需要考慮婚禮細節,這就是代理模式給我們帶來的好處。我們不必在目標對象內寫清楚每一件事,而是可以交給代理對象代理從而對目標對象進行擴展。

靜態代理


如果按照上面的例子該如何組織我們的代碼?
我們在組織我們的代碼先,先要了解下面這幾個概念。

角色 作用
抽象角色 聲明真實對象和代理對象的共同接口。
代理角色 代理對象角色內部含有對真實對象的引用,從而可以操作真實對象,同時代理對象提供與真實對象相同的接口以便在任何時刻都能夠代替真實對象。同時,代理對象可以在執行真實對象操作時,附加其他的操作,相當于對真實對象進行封裝。
真實角色 代理角色所代表的真實對象,是我們最終要引用的對象。

實現細節
Step 1:聲明抽象角色。

public interface Marry {
    public void marry();
}

Step 2:真實角色實現抽象角色接口。

@Override
public void marry() {
    System.out.println(this.getClass().getSimpleName() + "結婚啦");
}

Step 3:代理角色持有真實角色引用,實現抽象角色接口,附加其他操作。

private Walidake walidake;

public WeddingCompany(Walidake walidake) {
    this.walidake = walidake;
}

@Override
public void marry() {
    System.out.println("婚禮籌備");
    
    walidake.marry();
    
    System.out.println("婚禮結束");
    
}

Step 4:實際使用中,調用代理角色的方法對真實角色進行代理。

public static void main(String[] args) {
    Walidake walidake = new Walidake();
    WeddingCompany weddingCompany = new WeddingCompany(walidake);
    weddingCompany.marry();
}

從上述我們可以總結出:
1.代理角色,真實角色需要實現同一個抽象角色(接口)
2.代理角色需要持有真實角色的引用

上述代理方式我們稱之為靜態代理。那么有靜態代理,那是不是也應該有動態代理?
答案是肯定的。不過,我們并不著急著匆匆進入動態代理的學習,我們先想想在日常編碼中有沒有看到過靜態代理的例子?
我想你必定使用過Java的線程,對Runnable和Thread也很熟悉,而你現在你回過頭去看,是否能發現Thread implements Runnable,這其實就是一個代理角色,而我們new Runnable,這就是一個真實角色,賦值給Thread,這是不是就讓代理角色持有真實角色的引用。因此,Thread和Runnable這種關系也是靜態代理。

動態代理


動態代理不同于靜態代理的特點是它更為靈活,因為動態代理就是在運行期間動態生成代理類。我們沿用上面的例子,假設有五百個不一樣的人要結婚,都交給婚慶公司來操辦,那么按照靜態代理的思路來做,我們需要寫五百個真實角色,并且代理角色持有這五百個真實角色。這顯然不合邏輯。這時候動態代理就應運而生了。

動態代理分兩種,一種是基于接口實現的Java Proxy(Java自帶的),一種是基于繼承實現的cglib代理。下面會分別給出一個小demo,并且從源碼解析角度來解析二者動態代理的實現。

Java Proxy


public class WeddingCompany implements InvocationHandler{

    private Object object;
    
    public WeddingCompany(Object object) {
        this.object = object;
    }
    
    @SuppressWarnings("unchecked")
    public <T> T getProxy(){
        return (T)Proxy.newProxyInstance(
                Thread.currentThread().getContextClassLoader(), 
                object.getClass().getInterfaces(), 
                this);
    }
    

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        if ("marry".equals(method.getName())) {
            System.out.println("婚禮籌備");
            
            method.invoke(object, args);
            
            System.out.println("婚禮結束");
        }
        return null;
    }

}

InvocationHandler相當于一個處理器,在invoke方法中我們能夠操作真實對象,可以附加其他操作。而我們通過Proxy.newProxyInstance(..)方法生成代理。下面invoke參數的解釋說明。

參數 說明
proxy 指代我們所代理的那個真實對象
method 指代的是我們所要調用真實對象的某個方法的Method對象
args 指代的是調用真實對象某個方法時接受的參數

實現InvocationHandler接口并附加操作后,獲取代理角色。

//第一個人
Walidake walidake = new Walidake();
Marry marry = new WeddingCompany(walidake).getProxy();
marry.marry();

System.out.println();

//第二個人
Other other = new Other();
Marry marry2 = new WeddingCompany(other).getProxy();
marry2.marry();

運行結果:

result.png

那么如此一來,不論是兩個人,還是五百人都可以通過同一個動態代理進行代理調用,這帶給我們極大的方便。而為什么動態代理能實現這樣的功能呢?
我們選擇Proxy.newInstance(...)作為入口查看源碼。

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    //將我們傳入的接口進行復制
    final Class<?>[] intfs = interfaces.clone();

    //得到proxy代理類
    Class<?> cl = getProxyClass0(loader, intfs);
    //取得proxy代理類的構造器
    final Constructor<?> cons = cl.getConstructor(constructorParams);
    //取得InvocationHandler對象,即代理處理類對象
    final InvocationHandler ih = h;
    //判別是否為public類型
    if (!Modifier.isPublic(cl.getModifiers())) {
        //訪問控制
        AccessController.doPrivileged(new PrivilegedAction<Void>() {
            public Void run() {
                cons.setAccessible(true);
                return null;
            }
        });
    }
    //反射構造proxy類對象
    //由此可以推測,proxy的構造方法應該是 public Proxy(InvocationHandler handler)
    return cons.newInstance(new Object[]{h});
}

我們大體上走完了一個proxy代理流程(了解到其實也是通過反射來實現對類的代理)那么我們又得想,那如何能得到proxy的Class,點擊進入getProxyClass0(loader, intfs):

private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
    //接口最多不得超過65535
    //字節碼文件中嚴格規定,這點不必糾結
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }
    //從緩存中取得proxy類
    return proxyClassCache.get(loader, interfaces);
}

我們繼續追蹤下去(1.7和1.8代碼已發生變化,下面是針對1.8進行解析,1.7有疑惑的,可在評論區留言):

if (supplier != null) {
    // supplier might be a Factory or a CacheValue<V> instance:supplier可能是個Factory或者CacheValue
    //然后發現從其中取到了proxy類,說明應該定位到該方法內
    V value = supplier.get();
    if (value != null) {
        return value;
    }
}

定位到supplier.get()方法內:

//判斷從valueFactory得到的proxy Class是否為空
value = Objects.requireNonNull(valueFactory.apply(key, parameter));
return value;

點擊進入ProxyClassFactory中的apply(key, parameter)內(注意:該類是Proxy的內部類):

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

    Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
    for (Class<?> intf : interfaces) {
        Class<?> interfaceClass = null;
        try {
            //通過類加載器加載class到內存
            interfaceClass = Class.forName(intf.getName(), false, loader);
        } catch (ClassNotFoundException e) {
        }
        if (interfaceClass != intf) {
            throw new IllegalArgumentException(
                intf + " is not visible from class loader");
        }
        //判斷class是否接口
        if (!interfaceClass.isInterface()) {
            throw new IllegalArgumentException(
                interfaceClass.getName() + " is not an interface");
        }
        //沒有重復定義的接口
        if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
            throw new IllegalArgumentException(
                "repeated interface: " + interfaceClass.getName());
        }
    }
    
    //proxy Class所在包
    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) {
        // 等價于com.sun.proxy.
        proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
    }

    long num = nextUniqueNumber.getAndIncrement();
    //proxyPkg == null時,等價于com.sun.proxy.$proxy0,num會根據動態代理類的數量增加
    String proxyName = proxyPkg + proxyClassNamePrefix + num;

    //得到生成的字節碼
    byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
        proxyName, interfaces, accessFlags);
    try {
        //本地方法調用
        return defineClass0(loader, proxyName,
                            proxyClassFile, 0, proxyClassFile.length);
    } catch (ClassFormatError e) {
        throw new IllegalArgumentException(e.toString());
    }
}

一般的分析到這里就結束了,而我們應該更關注的是proxy類是如何組織生成的。因此,繼續進入ProxyGenerator.generateProxyClass方法中。

public static byte[] generateProxyClass(final String name,
        Class[] interfaces) {
    ProxyGenerator gen = new ProxyGenerator(name, interfaces);
    //生成字節碼細節
    final byte[] classFile = gen.generateClassFile();
    
    //將proxy代理類寫入到硬盤中
    if (saveGeneratedFiles) {
        java.security.AccessController.doPrivileged(new java.security.PrivilegedAction<Void>() {
            public Void run() {
                try {
                    FileOutputStream file = new FileOutputStream(
                            dotToSlash(name) + ".class");
                    file.write(classFile);
                    file.close();
                    return null;
                } catch (IOException e) {
                    throw new InternalError("I/O exception saving generated file: "+ e);
                }
            }
        });
    }

    return classFile;
}

查看更多細節:

private byte[] generateClassFile() {
    //生成proxy代理類的hashcode,equals,toString方法
    addProxyMethod(hashCodeMethod, Object.class);
    addProxyMethod(equalsMethod, Object.class);
    addProxyMethod(toStringMethod, Object.class);

    //添加各個接口的方法
    //這就是為什么我們能夠通過代理調用接口方法實現的原因
    for (int i = 0; i < interfaces.length; i++) {
        Method[] methods = interfaces[i].getMethods();
        for (int j = 0; j < methods.length; j++) {
            addProxyMethod(methods[j], interfaces[i]);
        }
    }

    //檢查返回類型
    for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
        checkReturnTypes(sigmethods);
    }

    //編譯成class的相關內容
    try {
        methods.add(generateConstructor());

        for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
            for (ProxyMethod pm : sigmethods) {
                fields.add(new FieldInfo(pm.methodFieldName,
                        "Ljava/lang/reflect/Method;", ACC_PRIVATE
                                | ACC_STATIC));

                methods.add(pm.generateMethod());
            }
        }

        methods.add(generateStaticInitializer());

    } catch (IOException e) {
        throw new InternalError("unexpected I/O Exception");
    }

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

    //將組織好的class文件寫入到文件中
    cp.getClass(dotToSlash(className));
    cp.getClass(superclassName);
    for (int i = 0; i < interfaces.length; i++) {
        cp.getClass(dotToSlash(interfaces[i].getName()));
    }

    //設置為只讀模式
    cp.setReadOnly();

    ByteArrayOutputStream bout = new ByteArrayOutputStream();
    DataOutputStream dout = new DataOutputStream(bout);

    try {
        //以下是class文件的結構,想深入了解的話可以看深入Java虛擬機
        //或者可以留意我后面相應的文章
        // 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(ACC_PUBLIC | ACC_FINAL | ACC_SUPER);
        // 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 (int i = 0; i < interfaces.length; i++) {
            dout.writeShort(cp.getClass(dotToSlash(interfaces[i].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");
    }

    return bout.toByteArray();
}

小結
到這里,我們就解析完了Java Proxy。從代碼中我們也不難看出真實角色要實現抽象角色的目的,就是為了底層能夠生成class字節碼從而生成代理角色,對真實角色進行代理。

更多
為了更好地了解Proxy代理類的結構(順便加深印象),我們不妨通過一個Generator類來生成相應的class文件:

public static void main(String[] args) {
    createProxyClassFile();
}

public static void createProxyClassFile() {
    String name = "CustomProxy";
    byte[] data = sun.misc.ProxyGenerator.generateProxyClass(name,
            new Class[] { Walidake.class });
    try {
        FileOutputStream out = new FileOutputStream(name + ".class");
        out.write(data);
        out.close();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

通過反編譯可以得到:

public final class CustomProxy extends Proxy implements Marry {
    private static Method m1;
    private static Method m2;
    private static Method m0;
    private static Method m3;

    public CustomProxy(InvocationHandler paramInvocationHandler) {
        super(paramInvocationHandler);
    }

    public final boolean equals(Object paramObject) {
        try {
            return ((Boolean) this.h.invoke(this, m1,
                    new Object[] { paramObject })).booleanValue();
        } catch (RuntimeException localRuntimeException) {
            throw localRuntimeException;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

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

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

    public final void marry() {
        try {
            //最最最重要的就是marry方法中的invoke方法
            //調用了自定義InvocationHandler的invoke方法
            //這是能實現動態代理的根本原因
            this.h.invoke(this, m3, null);
            return;
        } catch (RuntimeException localRuntimeException) {
            throw localRuntimeException;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals",
                    new Class[] { Class.forName("java.lang.Object") });
            m2 = Class.forName("java.lang.Object").getMethod("toString",
                    new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode",
                    new Class[0]);
            m3 = Class.forName("com.walidake.dynamic_proxy.proxy.Marry")
                    .getMethod("marry", new Class[0]);
        } catch (NoSuchMethodException localNoSuchMethodException) {
            throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        } catch (ClassNotFoundException localClassNotFoundException) {
            throw new NoClassDefFoundError(
                    localClassNotFoundException.getMessage());
        }
    }
}

既然上述是基于接口實現的代理,那我們可不可以不寫接口而是直接寫一個類,然后也實現代理的功能呢?
我們說過,有兩種動態代理的方式。cglib就是不通過接口也能實現動態代理的代理方式。

cglib


cglib是一個強大的高性能的代碼生成包,可以為那些沒有接口的類創建模仿(moke)對象。上一小節我們說到Java Proxy是通過生成字節碼,再把類加載進內存后實現Proxy進行動態代理的。同樣地,cglib也是通過生成操作字節碼的技術實現動態代理的。但與前者不同的是它并不直接操作字節碼,而是通過一個小而快的字節碼處理框架ASM(Java字節碼操控框架),來轉換字節碼并生成新的類。因此,cglib包要依賴于asm包,需要一起導入。

依然沿用上面的例子,這次我們不使用接口實現的方式。

實現細節:

public class WeddingCompany implements MethodInterceptor {

    
    @SuppressWarnings("unchecked")
    public <T> T getProxy(Class<T> clazz) {
        Enhancer en = new Enhancer();     
         //進行代理     
         en.setSuperclass(clazz);     
         en.setCallback(this);     
         //生成代理實例     
         return (T)en.create();     
     } 

    @Override
    public Object intercept(Object object, Method method, Object[] args,
            MethodProxy methodProxy) throws Throwable {
        Object result = null;
        
        if ("marry".equals(method.getName())) {
            System.out.println("婚禮籌備");
            
            //通過繼承的方法實現代理,因此這里調用的是invokeSuper
            result = methodProxy.invokeSuper(object, args);
            
            System.out.println("婚禮結束");
        }
        return result;
    }

}

MethodInterceptor是方法攔截器,我們能在這里做真實對象的附加操作,object就是我們的真實對象,method、args就是真實對象調用的方法和參數,methodProxy是方法代理。原來的方法可能通過使用java.lang.reflect.Method對象的一般反射調用,或者使用 net.sf.cglib.proxy.MethodProxy對象調用。net.sf.cglib.proxy.MethodProxy通常被首選使 用,因為它更快。在這個方法中,我們可以在調用原方法之前或之后注入自己的代碼。

在這個例子中,我們通過Enhancer的無參數構造器時用來創建target實例,并使用setSuperClass傳入代理的父類,setCallback決定方法回調。

調用細節:

public static void main(String[] args) {
        Walidake proxy = new WeddingCompany().getProxy(Walidake.class);
        proxy.marry();
}

運行結果:

result2.png

顯然我們看到的是Walidake$$EnhancerByCGLIB$$d8d9733e,而不是Walidake。這說明cglib代理類成功代理了真實角色。

接下來,我們選取Enhancer的create()作為入口,查看源碼:

public Object create() {
    //生成代理類是否只讀
    classOnly = false;
    argumentTypes = null;
    
    return createHelper();
}

點擊進入createHelper():

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;
}

點擊進入super.create(key):

protected Object create(Object key) {
    try {
        //取得類加載器
        ClassLoader loader = getClassLoader();
        Map<ClassLoader, ClassLoaderData> cache = CACHE;
        ClassLoaderData data = cache.get(loader);
        
        ...
        //從類加載器數據中取出cglib代理類
        Object obj = data.get(this, getUseCache());
        if (obj instanceof Class) {
            //實例化代理類對象
            return firstInstance((Class) obj);
        }
        //因為緩存了反射實例,能夠快速實例化
        return nextInstance(obj);
}

進入data.get(this, getUseCache()),發現這樣一行:

return gen.generate(ClassLoaderData.this);

繼續進行追蹤:

protected Class generate(ClassLoaderData data) {
    synchronized (classLoader) {
      //取得cglib代理類類名
      //實現細節:DefaultNamingPolicy.getClassName(String, String, Object, Predicate)中
      //不多做擴展,可以自己看看
      String name = generateClassName(data.getUniqueNamePredicate());              
      data.reserveName(name);
      this.setClassName(name);
    }
    
    //生成cglib代理類字節碼
    byte[] b = strategy.generate(this);
    String className = ClassNameReader.getClassName(new ClassReader(b));
    ProtectionDomain protectionDomain = getProtectionDomain();
    synchronized (classLoader) { // just in case
        //通過類加載器加載代理Class
        if (protectionDomain == null) {
            gen = ReflectUtils.defineClass(className, b, classLoader);
        } else {
            gen = ReflectUtils.defineClass(className, b, classLoader, protectionDomain);
        }
    }
    return gen;
}

離真相越來越近,點擊進入generate(this):

transform(cg).generateClass(cw);

點擊進入generateClass(cw),注意這時候我們又回到了Enhancer類:

public void generateClass(ClassVisitor v) throws Exception {
    //這就是設置setSuperClass的原因
    //讓cglib代理類通過繼承的方式進行代理
    Class sc = (superclass == null) ? Object.class : superclass;

    if (TypeUtils.isFinal(sc.getModifiers()))
        throw new IllegalArgumentException("Cannot subclass final class " + sc.getName());
    List constructors = new ArrayList(Arrays.asList(sc.getDeclaredConstructors()));
    //過濾構造器
    filterConstructors(sc, constructors);

    List actualMethods = new ArrayList();
    List interfaceMethods = new ArrayList();
    final Set forcePublic = new HashSet();
    //確定需要添加的所有方法
    getMethods(sc, interfaces, actualMethods, interfaceMethods, forcePublic);

    List methods = CollectionUtils.transform(actualMethods, new Transformer() {
        public Object transform(Object value) {
            Method method = (Method)value;
            int modifiers = Constants.ACC_FINAL
                | (method.getModifiers()
                   & ~Constants.ACC_ABSTRACT
                   & ~Constants.ACC_NATIVE
                   & ~Constants.ACC_SYNCHRONIZED);
            if (forcePublic.contains(MethodWrapper.create(method))) {
                modifiers = (modifiers & ~Constants.ACC_PROTECTED) | Constants.ACC_PUBLIC;
            }
            return ReflectUtils.getMethodInfo(method, modifiers);
        }
    });

    ClassEmitter e = new ClassEmitter(v);
    //寫入類信息
    if (currentData == null) {
    e.begin_class(Constants.V1_2,
                  Constants.ACC_PUBLIC,
                  getClassName(),
                  Type.getType(sc),
                  (useFactory ?
                   TypeUtils.add(TypeUtils.getTypes(interfaces), FACTORY) :
                   TypeUtils.getTypes(interfaces)),
                  Constants.SOURCE_FILE);
    } else {
        e.begin_class(Constants.V1_2,
                Constants.ACC_PUBLIC,
                getClassName(),
                null,
                new Type[]{FACTORY},
                Constants.SOURCE_FILE);
    }
    ...具體的字節碼操作細節
    //結束寫入
    e.end_class();
}

小結
到這里,cglib代理類的解析也就結束了。我們現在知道為什么說它是基于繼承的代理方式,從源代碼中我們就可以看出來。

更多
同樣地,我們也打印出cglib生成類的結構。使用了反射,只能打印出一個輪廓,而不能打印完整的內容。(試過javassist,classloader加載,各種谷歌都沒能找出較好的方案,暫時只能使用反射做):

public class Walidake$$EnhancerByCGLIB$$d8d9733e extends com.walidake.dynamic_proxy.cglib.Walidake {
  public Walidake$$EnhancerByCGLIB$$d8d9733e();

  public final boolean equals(Object);

  public final java.lang.String toString();

  public final int hashCode();

  protected final java.lang.Object clone();

  public java.lang.Object newInstance(Callback[]);

  public java.lang.Object newInstance(Class[], Object[], Callback[]);

  public java.lang.Object newInstance(Callback);

  public final void marry();

  public void setCallback(int, Callback);

  public void setCallbacks(Callback[]);

  public static void CGLIB$SET_STATIC_CALLBACKS(Callback[]);

  public static void CGLIB$SET_THREAD_CALLBACKS(Callback[]);

  public net.sf.cglib.proxy.Callback getCallback(int);

  public [Lnet.sf.cglib.proxy.Callback; getCallbacks();

  final java.lang.Object CGLIB$clone$4();

  final void CGLIB$marry$0();

  final boolean CGLIB$equals$1(Object);

  final java.lang.String CGLIB$toString$2();

  final int CGLIB$hashCode$3();

  static void CGLIB$STATICHOOK1();

  private static final void CGLIB$BIND_CALLBACKS(Object);

  public static net.sf.cglib.proxy.MethodProxy CGLIB$findMethodProxy(Signature);


  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$marry$0$Method;
  private static final MethodProxy CGLIB$marry$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;
}

從這里我們能更直觀地看出cglib是通過繼承原先的真實角色生成代理類。

再擴展
cglib也可以基于接口實現,可以關注Mixin這個類。另外值得注意的是CallbackFilter,可以實現相應的權限控制。簡略提一下,后面可以看看。

因此,我們現在知道兩種動態代理類都是基于字節碼的操作生成相應的代理類,不同的是Java Proxy是基于接口實現的,而cglib是基于繼承實現的。而值得一提的是,

總結


我們把代理分為兩種:靜態代理和動態代理。

應用場景
靜態代理主要用來處理少部分類的托管或者擴展。靜態代理對于被代理的對象很固定,我們只需要去代理一個類或者若干固定的類,數量不是太多的時候,可以使用,而且其實效果比動態代理更好。
動態代理在運行期間動態生成代理類,需要消耗的時間會更久一點。優點是可以做很多類的擴展(譬如可以通過動態代理實現aop,hibernate使用cglib來代理單端多對一和一對一關聯等)。而且如果一個類的接口發生了變化,那么靜態代理這時候修改起來就很麻煩了。這就是說動態代理靈活的原因。

動態代理主流的實現有兩種,一種是基于接口的Java Proxy的代理機制,一種是基于繼承的cglib代理機制。兩種也都有其應用場景。(視乎具體業務邏輯而言)

代理模式更多的應用場景(了解即可)

  1. 創建開銷大的對象時候,比如顯示一幅大的圖片,我們將這個創建的過程交給代理去完成,我們稱之為虛代理(Virtual Proxy)。
  2. 為網絡上的對象創建一個局部的本地代理,比如要操作一個網絡上的一個對象(網絡性能不好的時候,問題尤其突出),我們將這個操縱的過程交給一個代理去完成,我們GoF稱之為遠程代理(Remote Proxy)。
  3. 對對象進行控制訪問的時候,比如在Jive論壇中不同權限的用戶(如管理員、普通用戶等)將獲得不同層次的操作權限,我們將這個工作交給一個代理去完成,我們稱之為保護代理(Protection Proxy)。
  4. 當一個對象被引用時,提供一些額外的操作,比如將對此對象調用的次數記錄下來等。是指當調用真實的對象時,代理處理另外一些事。如計算真實對象的引用次數,這樣當該對象沒有引用時,可以自動釋放它;或當第一次引用一個持久對象時,將它裝入內存;或在訪問一個實際對象前,檢查是否已經鎖定它,以確保其他對象不能改變它。它們都是通過代理在訪問一個對象時附加一些內務處理。又稱智能引用(Smart Reference)代理。
    在所有種類的代理模式中,虛擬(Virtual)代理、遠程(Remote)代理、智能引用代理(Smart Reference Proxy)和保護(Protect or Access)代理是最為常見的代理模式。

至此,代理模式全部解析完。

項目地址:https://github.com/walidake/proxy

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,908評論 6 541
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,324評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 178,018評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,675評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,417評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,783評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,779評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,960評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,522評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,267評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,471評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,009評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,698評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,099評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,386評論 1 294
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,204評論 3 398
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,436評論 2 378

推薦閱讀更多精彩內容