深入分析Spring源碼(二)----代理模式(jdk動態代理)

代理模式:

特點:1.有執行者,被代理人兩個角色

           2.對於被代理人來說,這件事情是一定要做的,但自己又不想做或者沒有時間做,找代理

           3.需要獲取被代理人的個人資料

首先來看一下如何使用JDK動態代理。JDK提供了java.lang.reflect.Proxy類來實現動態代理的,可通過它的newProxyInstance來獲得代理實現類。同時對於代理的接口的實際處理,是一個java.lang.reflect.InvocationHandler,它提供了一個invoke方法供實現者提供相應的代理邏輯的實現。可以對實際的實現進行一些特殊的處理,像Spring AOP中的各種advice。

//靜態代理

public interface Person{
    void sing();       
    String getName();
    
    String getSex(); 
}

public class Star implements Person{
    private String name = "Gongxiaoxiao";
    private String sex = "female";
    @Override
     public  void sing(){
        System.out.println("===Star====Sing===");
    }
}

public class Agent implements Person{
    private Person p;

    Agent(Person p ){
            this.p = p;
    }

    @Override
    public void sing(){
        System.out.println("====唱歌前談價錢===");
        p.sing();
        System.out.println("======唱歌后護送離場====");
    }
}

public static void main(String[] args){
        Person p = new Star();
        Agent aa = new Agent(p);
        aa.sing();
}
//動態代理

public interface Person{
  public  void sing();        
}

public class Star implements Person{
    @Override
     public  void sing(){
        System.out.println("===Star====Sing===");
    }
}

public class Agent implements InvocationHandler{

       private Person target;
    //獲取被代理人的個人資料
      public Object getInstance(Person target) throws Exception{
           this.target = target;
            //Proxy.newProxyInstance(ClassLoader loader,Class<?>            
            //    []interfaces,InvocationHandler h);
           Class clazz = target.getClass();
           //參數1:被代理人對應的類加載器,參數2:被代理人對應實現的接口,參數3:代理人;返回代    
           //理對象    
           //類加載器(ClassLoader)用來加載動態代理類
           //一個要實現接口的數組,從這點就可以看出,要想使用JDK動態代理,必須要有接口類
           return     
             Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this); 
      }

    @Override
    public Object invoke(Object proxy,Method method,Object[]args){
        System.out.println("=====付出場費=====");
        method.invoke(target,args);
        System.out.println("=====護送離場=====");
    }   
}

public static void main(String[] args){
     //設置sun.misc.ProxyGenerator.saveGeneratedFiles這個系統屬性爲true來把生成的class保存到 
     //本地文件來查看。
     System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
     Person p = new Star();
     Person aa = (Person)Agent.getInstance(p);
     //該代理對象的引用aa的運行時類爲 $Proxy0
     System.out.println(aa.getClass());    
     aa.sing();
}
  public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException {
        if (h == null) {
            throw new NullPointerException();
        }
 
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        // 這裏是生成class的地方
        Class<?> cl = getProxyClass0(loader, intfs);
        // 使用我們實現的InvocationHandler作爲參數調用構造方法來獲得代理類的實例
        try {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }
其中newInstance只是調用Constructor.newInstance來構造相應的代理類實例,這裏重點是看getProxyClass0這個方法的實現:
 private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        // 代理的接口數量不能超過65535(沒有這種變態吧)
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        // JDK對代理進行了緩存,如果已經存在相應的代理類,則直接返回,否則纔會通過ProxyClassFactory來創建代理
        return proxyClassCache.get(loader, interfaces);
    }
其中代理緩存是使用WeakCache實現的,如下
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
具體的緩存邏輯這裏暫不關心,只需要關心ProxyClassFactory是如何生成代理類的,ProxyClassFactory是Proxy的一個靜態內部類,實現了WeakCache的內部接口BiFunction的apply方法:
    private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
        // 所有代理類名字的前綴
        private static final String proxyClassNamePrefix = "$Proxy";
 
        // 用於生成代理類名字的計數器
        private static final AtomicLong nextUniqueNumber = new AtomicLong();
 
        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
            // 省略驗證代理接口的代碼……
 
            String proxyPkg = null;     // 生成的代理類的包名
            // 對於非公共接口,代理類的包名與接口的相同
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    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");
                    }
                }
            }
 
            // 對於公共接口的包名,默認爲com.sun.proxy
            if (proxyPkg == null) {
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
 
            // 獲取計數
            long num = nextUniqueNumber.getAndIncrement();
            // 默認情況下,代理類的完全限定名爲:com.sun.proxy.$Proxy0,com.sun.proxy.$Proxy1……依次遞增
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
 
            // 這裏纔是真正的生成代理類的字節碼的地方
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces);
            try {
                // 根據二進制字節碼返回相應的Class實例
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
    }


ProxyGenerator是sun.misc包中的類,它沒有開源,但是可以反編譯來一探究竟:
    public static byte[] generateProxyClass(final String var0, Class[] var1) {
        ProxyGenerator var2 = new ProxyGenerator(var0, var1);
        final byte[] var3 = var2.generateClassFile();
        // 這裏根據參數配置,決定是否把生成的字節碼(.class文件)保存到本地磁盤,我們可以通過把相應的class文件保存到本地,再反編譯來看看具體的實現,這樣更直觀
        if(saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction() {
                public Void run() {
                    try {
                        FileOutputStream var1 = new FileOutputStream(ProxyGenerator.dotToSlash(var0) + ".class");
                        var1.write(var3);
                        var1.close();
                        return null;
                    } catch (IOException var2) {
                        throw new InternalError("I/O exception saving generated file: " + var2);
                    }
                }
            });
        }
        return var3;
    }

對生成的代理類進行反編譯可得:

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

package com.sun.proxy;

import com.example.demo1.dynamicProxy.Person;
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 Person {
    private static Method m1;
    private static Method m4;
    private static Method m3;
    private static Method m2;
    private static Method m5;
    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 getSex() throws  {
        try {
            return (String)super.h.invoke(this, m4, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

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

    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 sing() throws  {
        try {
            super.h.invoke(this, m5, (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"));
            m4 = Class.forName("com.example.demo1.dynamicProxy.Person").getMethod("getSex");
            m3 = Class.forName("com.example.demo1.dynamicProxy.Person").getMethod("getName");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m5 = Class.forName("com.example.demo1.dynamicProxy.Person").getMethod("sing");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

生成的動態代理類$Proxy0的特性:

       繼承了Proxy類,實現了代理的接口,由於java不能多繼承,這裏已經繼承了Proxy類了,不能再繼承其他的類,所以JDK的動態代理不支持對實現類的代理,只支持接口的代理。
提供了一個使用InvocationHandler作爲參數的構造方法。
生成靜態代碼塊來初始化接口中方法的Method對象,以及Object類的equals、hashCode、toString方法。
重寫了Object類的equals、hashCode、toString,它們都只是簡單的調用了InvocationHandler的invoke方法,即可以對其進行特殊的操作,也就是說JDK的動態代理還可以代理上述三個方法。
代理類實現代理接口的sing方法中,只是簡單的調用了InvocationHandler的invoke方法,我們可以在invoke方法中進行一些特殊操作,甚至不調用實現的方法,直接返回。

至此JDK動態代理的實現原理就分析的差不多了。同時我們可以想像一下Spring AOP提供的各種攔截該如何實現,就已經很明瞭了,如下所示:

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // BeforeAdvice
        Object retVal = null;
        try {
            // AroundAdvice
            retVal = method.invoke(target, args);
            // AroundAdvice
            // AfterReturningAdvice
        }
        catch (Throwable e) {
            // AfterThrowingAdvice
        }
        finally {
            // AfterAdvice
        }
        return retVal;
    }

(源碼分析部分來自於https://blog.csdn.net/mhmyqn/article/details/48474815

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章