二:代理模式(靜態代理,動態代理)

首先代理模式,能夠分爲兩種,一種是靜態代理,一種是動態代理。html

               兩種代理從虛擬機加載類的角度來說,本質上都是同樣的,都是在原有類的行爲基礎上,加入一些多出的行爲,甚至徹底替換原有的行爲。java

               靜態代理採用的方式就是咱們手動的將這些行爲換進去,而後讓編譯器幫咱們編譯,同時也就將字節碼在原有類的基礎上加入一些其餘的東西或者替換原有的東西,產生一個新的與原有類接口相同卻行爲不一樣的類型。mysql

               說歸說,咱們來真實的去試驗一下,實驗的話須要找一個示例,就拿咱們的數據庫鏈接來作例子吧。程序員

               咱們都知道,數據庫鏈接是很珍貴的資源,頻繁的開關數據庫鏈接是很是浪費服務器的CPU資源以及內存的,因此咱們通常都是使用數據庫鏈接池來解決這一問題,即創造一堆等待被使用的鏈接,等到用的時候就從池裏取一個,不用了再放回去,數據庫鏈接在整個應用啓動期間,幾乎是不關閉的,除非是超過了最大閒置時間。spring

               可是在程序員編寫程序的時候,會常用connection.close()這樣的方法,去關閉數據庫鏈接,並且這樣作是對的,因此你並不能告訴程序員們說,大家使用鏈接都不要關了,去調用一個其餘的相似歸還給鏈接池的方法吧。這是不符合程序員的編程思惟的,也很勉強,並且具備風險性,由於程序員會忘的。sql

               解決這一問題的辦法就是使用代理模式,由於代理模式能夠替代原有類的行爲,因此咱們要作的就是替換掉connection的close行爲。數據庫

               下面是connection接口原有的樣子,我去掉了不少方法,由於都相似,全貼上來佔地方。編程

複製代碼
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Wrapper;

public interface Connection  extends Wrapper {
    
    Statement createStatement() throws SQLException;
    
    void close() throws SQLException;
    
}
複製代碼

              這裏只貼了兩個方法,可是咱們代理的精髓只要兩個方法就能掌握,下面使用靜態代理,採用靜態代理咱們一般會使用組合的方式,爲了保持對程序猿是透明的,咱們實現Connection這個接口, 以下所示。api

複製代碼
import java.sql.SQLException;
import java.sql.Statement;


public class ConnectionProxy implements Connection{
    
    private Connection connection;
    
    public ConnectionProxy(Connection connection) {
        super();
        this.connection = connection;
    }

    public Statement createStatement() throws SQLException{
        return connection.createStatement();
    }
    
    public void close() throws SQLException{
        System.out.println("不真正關閉鏈接,歸還給鏈接池");
    }

}
複製代碼

                咱們在構造方法中讓調用者強行傳入一個原有的鏈接,接下來咱們將咱們不關心的方法,交給真正的Connection接口去處理,就像createStatement方法同樣,而咱們將真正關心的close方法用咱們本身但願的方式去進行。數組

                此處爲了更形象,LZ給出一個本人寫的很是簡單的鏈接池,意圖在於代表實現的思路。下面咱們來看一下鏈接池的變化,在裏面註明了變化點。

複製代碼
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.LinkedList;

public class DataSource {
    
    private static LinkedList<Connection> connectionList = new LinkedList<Connection>();
    
    static{
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    
    private static Connection createNewConnection() throws SQLException{
        return DriverManager.getConnection("url","username", "password");
    }
    
    private DataSource(){
        if (connectionList == null || connectionList.size() == 0) {
            for (int i = 0; i < 10; i++) {
                try {
                    connectionList.add(createNewConnection());
                } catch (SQLException e) {
                    e.printStackTrace();
                }
            }
        }
    }
    
    public Connection getConnection() throws Exception{
        if (connectionList.size() > 0) {
            //return connectionList.remove();  這是原有的方式,直接返回鏈接,這樣可能會被程序員把鏈接給關閉掉
            //下面是使用代理的方式,程序員再調用close時,就會歸還到鏈接池
            return new ConnectionProxy(connectionList.remove());
        }
        return null;
    }
    
    public void recoveryConnection(Connection connection){
        connectionList.add(connection);
    }
    
    public static DataSource getInstance(){
        return DataSourceInstance.dataSource;
    }
    
    private static class DataSourceInstance{
        
        private static DataSource dataSource = new DataSource();
        
    }
    
}
複製代碼

               鏈接池咱們把它作成單例,因此假設是上述鏈接池的話,咱們代理中的close方法能夠再具體化一點,就像下面這樣,用歸還給鏈接池的動做取代關閉鏈接的動做。

    public void close() throws SQLException{
        DataSource.getInstance().recoveryConnection(connection);
    }

               好了,這下咱們的鏈接池返回的鏈接全是代理,就算程序員調用了close方法也只會歸還給鏈接池了。

               咱們使用代理模式解決了上述問題,從靜態代理的使用上來看,咱們通常是這麼作的。

               1,代理類通常要持有一個被代理的對象的引用。

               2,對於咱們不關心的方法,所有委託給被代理的對象處理。

               3,本身處理咱們關心的方法。

               這種代理是死的,不會在運行時動態建立,由於咱們至關於在編譯期,也就是你按下CTRL+S的那一刻,就給被代理的對象生成了一個不可動態改變的代理類。

               靜態代理對於這種,被代理的對象很固定,咱們只須要去代理一個類或者若干固定的類,數量不是太多的時候,可使用,並且其實效果比動態代理更好,由於動態代理就是在運行期間動態生成代理類,因此須要消耗的時間會更久一點。就像上述的狀況,其實就比較適合使用靜態代理。

               下面介紹下動態代理,動態代理是JDK自帶的功能,它須要你去實現一個InvocationHandler接口,而且調用Proxy的靜態方法去產生代理類。

               接下來咱們依然使用上面的示例,可是此次該用動態代理處理,咱們來試一下看如何作。

複製代碼
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;


public class ConnectionProxy implements InvocationHandler{
    
    private Connection connection;
    
    public ConnectionProxy(Connection connection) {
        super();
        this.connection = connection;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //這裏判斷是Connection接口的close方法的話
        if (Connection.class.isAssignableFrom(proxy.getClass()) && method.getName().equals("close")) {
            //咱們不執行真正的close方法
            //method.invoke(connection, args);
            //將鏈接歸還鏈接池
            DataSource.getInstance().recoveryConnection(connection);
            return null;
        }else {
            return method.invoke(connection, args);
        }
    }
    
    public Connection getConnectionProxy(){
        return (Connection) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{Connection.class}, this);
    }

//JDK實現代理只須要使用newProxyInstance方法,可是該方法須要接收三個參數(參數this=InvocationHandler接口)
  public Object getProxy(){
  //static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
  return Proxy.newProxyInstance(source.getClass().getClassLoader(), source.getClass().getInterfaces(), this);
}


//給目標對象生成代理對象(舉例子:這是匿名內部類實現 InvocationHandler接口)
  public Object getProxyInstance(){
   return Proxy.newProxyInstance(getClass().getClassLoader(), getClass().getInterfaces(),
   new InvocationHandler() {
   @Override
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
   System.out.println("開始事務2");
   Object returnValue = method.invoke("", args);
   System.out.println("提交事務2");
   return returnValue;
   }
  }
  );
 }

}

複製代碼

               上面是咱們針對connection寫的動態代理,InvocationHandler接口只有一個invoke方法須要實現,這個方法是用來在生成的代理類用回調使用的,關於動態代理的原理一會作詳細的分析,這裏咱們先只關注用法。很顯然,動態代理是將每一個方法的具體執行過程交給了咱們在invoke方法裏處理。而具體的使用方法,咱們只須要創造一個ConnectionProxy的實例,而且將調用getConnectionProxy方法的返回結果做爲數據庫鏈接池返回的鏈接就能夠了。

               上述即是咱們針對connection作動態代理的方式,可是咱們從中得不到任何好處,除了能少寫點代碼之外,由於這個動態代理仍是隻能代理Connection這一個接口,若是咱們寫出這種動態代理的方式的話,說明咱們應該使用靜態代理處理這個問題,由於它表明咱們其實只但願代理一個類就好。從重構的角度來講,其實更簡單點,那就是在你發現你使用靜態代理的時候,須要寫一大堆重複代碼的時候,就請改用動態代理試試吧。

               一般狀況下,動態代理的使用是爲了解決這樣一種問題,就是咱們須要代理一系列類的某一些方法,最典型的應用就是咱們前段時間討論過的springAOP,咱們須要創造出一批代理類,切入到一系列類當中的某一些方法中。下面給出一個常用的動態代理方式。

複製代碼
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class DynamicProxy implements InvocationHandler{
    
    private Object source;
    
    public DynamicProxy(Object source) {
        super();
        this.source = source;
    }
    
    public void before(){
        System.out.println("在方法前作一些事,好比打開事務");
    }
    
    public void after(){
        System.out.println("在方法返回前作一些事,好比提交事務");
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //假設咱們切入toString方法,其餘其實也是相似的,通常咱們這裏大部分是針對特定的方法作事情的,一般不會對類的所有方法切入
        //好比咱們經常使用的事務管理器,咱們一般配置的就是對save,update,delete等方法纔打開事務
        if (method.getName().equals("toString")) {
            before();
        }
        Object result = method.invoke(source, args);
        if (method.getName().equals("toString")) {
            after();
        }
        return result;
    }
    
    public Object getProxy(){
        return Proxy.newProxyInstance(getClass().getClassLoader(), source.getClass().getInterfaces(), this);
    }
    
    
}
複製代碼

                上述我作了一些註釋,其實已經說明一些問題,這個代理類的做用是能夠代理任何類,由於它被傳入的對象是Object,而再也不是具體的類,好比剛纔的Connection,這些產生的代理類在調用toString方法時會被插入before方法和after方法。

                動態代理有一個強制性要求,就是被代理的類必須實現了某一個接口,或者自己就是接口,就像咱們的Connection。

                道理其實很簡單,這是由於動態代理生成的代理類是繼承Proxy類的,而且會實現被你傳入newProxyInstance方法的全部接口,因此咱們能夠將生成的代理強轉爲任意一個代理的接口或者Proxy去使用,可是Proxy裏面幾乎全是靜態方法,沒有實例方法,因此轉換成Proxy意義不大,幾乎沒什麼用。假設咱們的類沒有實現任何接口,那麼就意味着你只能將生成的代理類轉換成Proxy,那麼就算生成了,其實也沒什麼用,並且就算你傳入了接口,能夠強轉,你也用不了這個沒有實現你傳入接口的這個類的方法。

               你可能會說,假設有個接口A,那我將接口A傳給newProxyInstance方法,並代理一個沒實現接口A的類B,但類B與接口A有同樣的方法能夠嗎?

               答案是能夠的,而且JDK的動態代理只認你傳入的接口,只要你傳入,你就能夠強轉成這個接口,這個一會解釋,可是你沒法在invoke方法裏調用method.invoke方法,也就是說,你只能所有替換A接口的方法,而不能使用類B中原有與接口A方法描述相同的方法,這是由於invoke中傳入的Method的class信息是接口A,而類B由於沒實現接口A,因此沒法執行傳入的Method,會拋出非法參數異常。

               下面我貼出測試代碼,各位能夠本身試一下,具體爲什麼會這樣是在後面解釋的,這裏再也不多作解釋。

              先是一個普通接口。

複製代碼
public interface TestInterface {

    void method1();
    
    void method2();
    
    void method3();
}
複製代碼

            而後是一個類,和接口如出一轍的方法,可是就是沒實現這個接口。

複製代碼
public class TestClass{

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

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

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

}
複製代碼

               下面是測試類。

複製代碼
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


public class DynamicProxy implements InvocationHandler{
    
    private Object source;
    
    public DynamicProxy(Object source) {
        super();
        this.source = source;
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("接口的方法所有變成這樣了");
        //這裏source是TestClass,可是咱們不能使用反射調用它的方法,像下面這樣,放開這一行會拋異常
        //return method.invoke(source, args);
        return null;
    }
    
    public static void main(String[] args) {
        //只要你傳入就能夠強轉成功
        TestInterface object =  (TestInterface) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{TestInterface.class}, new DynamicProxy(new TestClass()));
        object.method1();
        object.method2();
        object.method3();
    }
}
複製代碼

               上面咱們運行就會發現接口的方法所有都只能輸出一個很2的字符串了。若是是要繼續使用TestClass的方法也不是不行,只要你確認你傳入的類包括了全部你傳入的接口的方法,只是沒實現這些接口而已,那麼你能夠在invoke中這樣使用。

複製代碼
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Method sourceMethod = source.getClass().getDeclaredMethod(method.getName(), method.getParameterTypes());
        sourceMethod.setAccessible(true);
        Object result = sourceMethod.invoke(source, args);
        System.out.println("after");
        return result;
    }
複製代碼

                這就與你實現接口的表現行爲一致了,可是咱們原本就只須要一句method.invoke就能夠了,就由於沒實現接口就要多寫兩行,因此這種突破JDK動態代理必須實現接口的行爲就有點多此一舉了。由於你原本就實現了該接口的方法,只是差了那一句implements而已。

               上面寫這個例子只是爲了解釋LZ當初的疑惑,由於LZ曾一度認爲不實現接口就不能使用動態代理,如今想一想那時候LZ有點2,呵呵。

               好了,從如今開始,咱們開始詳細講解動態代理的原理,這算是進階篇,若是是新手的話,能夠跳過下面的內容,由於如今還不必知道這些,並且弄很差會越看越蒙,不過僅僅是LZ我的建議,你要有耐心,徹底能夠繼續看下去。

               接下來咱們結合源碼去看一下,代理類是如何產生的,首先固然就是要進入Proxy的newProxyInstance方法,這裏是產生代理的入口,源碼以下。

複製代碼
public static Object newProxyInstance(ClassLoader loader,
                      Class<?>[] interfaces,
                      InvocationHandler h)
    throws IllegalArgumentException
    {
    if (h == null) {
        throw new NullPointerException();
    }

    /*
     * Look up or generate the designated proxy class.
     */
    Class cl = getProxyClass(loader, interfaces);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        Constructor cons = cl.getConstructor(constructorParams);
        return (Object) cons.newInstance(new Object[] { h });
    } catch (NoSuchMethodException e) {
        throw new InternalError(e.toString());
    } catch (IllegalAccessException e) {
        throw new InternalError(e.toString());
    } catch (InstantiationException e) {
        throw new InternalError(e.toString());
    } catch (InvocationTargetException e) {
        throw new InternalError(e.toString());
    }
    }
複製代碼

               這個方法其實很簡單,首先獲取了代理類的運行時Class引用,而後調用了這個Class中的構造方法,這個構造方法只有一個參數,正是InvocationHandler接口,由此產生了一個代理類的實例。那麼關鍵的地方就在於如何獲取的代理類運行時的class信息的呢?咱們進入getProxyClass方法看一下。爲了方便起見,我直接加註釋,這個方法須要解釋的地方比較多。

複製代碼
public static Class<?> getProxyClass(ClassLoader loader,
            Class<?>... interfaces) throws IllegalArgumentException {
        //若是傳入的接口長度大於65535就拋出異常,我去你妹。。。
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
        
        Class proxyClass = null;

        /* collect interface names to use as key for proxy class cache */
        String[] interfaceNames = new String[interfaces.length];

        Set interfaceSet = new HashSet(); // for detecting duplicates

        for (int i = 0; i < interfaces.length; i++) {
            /*
             * Verify that the class loader resolves the name of this interface
             * to the same Class object.
             */
            String interfaceName = interfaces[i].getName();
            Class interfaceClass = null;
            try {
                //加載每個接口的運行時Class信息
                interfaceClass = Class.forName(interfaceName, false, loader);
            } catch (ClassNotFoundException e) {
            }
            //若是採用你傳入的類加載器載入的Class和你傳入的Class不相等則拋出異常
            if (interfaceClass != interfaces[i]) {
                throw new IllegalArgumentException(interfaces[i]
                        + " is not visible from class loader");
            }

            //若是你傳入的不是接口拋出異常
            /*
             * Verify that the Class object actually represents an interface.
             */
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(interfaceClass.getName()
                        + " is not an interface");
            }

            //若是你傳入的接口重複拋異常
            /*
             * Verify that this interface is not a duplicate.
             */
            if (interfaceSet.contains(interfaceClass)) {
                throw new IllegalArgumentException("repeated interface: "
                        + interfaceClass.getName());
            }
            interfaceSet.add(interfaceClass);

            interfaceNames[i] = interfaceName;
        }

        /*
         * Using string representations of the proxy interfaces as keys in the
         * proxy class cache (instead of their Class objects) is sufficient
         * because we require the proxy interfaces to be resolvable by name
         * through the supplied class loader, and it has the advantage that
         * using a string representation of a class makes for an implicit weak
         * reference to the class.
         */
        Object key = Arrays.asList(interfaceNames);

        /*
         * Find or create the proxy class cache for the class loader.
         */
        Map cache;
        synchronized (loaderToCache) {
            //這個是爲了存儲每個類加載器所載入過的代理接口的代理類
            cache = (Map) loaderToCache.get(loader);
            if (cache == null) {
                cache = new HashMap();
                loaderToCache.put(loader, cache);
            }
            /*
             * This mapping will remain valid for the duration of this method,
             * without further synchronization, because the mapping will only be
             * removed if the class loader becomes unreachable.
             */
        }

        /*
         * Look up the list of interfaces in the proxy class cache using the
         * key. This lookup will result in one of three possible kinds of
         * values: null, if there is currently no proxy class for the list of
         * interfaces in the class loader, the pendingGenerationMarker object,
         * if a proxy class for the list of interfaces is currently being
         * generated, or a weak reference to a Class object, if a proxy class
         * for the list of interfaces has already been generated.
         */
        synchronized (cache) {
            /*
             * Note that we need not worry about reaping the cache for entries
             * with cleared weak references because if a proxy class has been
             * garbage collected, its class loader will have been garbage
             * collected as well, so the entire cache will be reaped from the
             * loaderToCache map.
             */
            do {
                //檢查是否有生成好的代理
                Object value = cache.get(key);
                if (value instanceof Reference) {
                    proxyClass = (Class) ((Reference) value).get();
                }
                //有的話直接返回
                if (proxyClass != null) {
                    // proxy class already generated: return it
                    return proxyClass;
                //不然看一下這個代理類是否是正在構造中,是的話就在cache對象上等待
                } else if (value == pendingGenerationMarker) {
                    // proxy class being generated: wait for it
                    try {
                        cache.wait();
                    } catch (InterruptedException e) {
                        /*
                         * The class generation that we are waiting for should
                         * take a small, bounded time, so we can safely ignore
                         * thread interrupts here.
                         */
                    }
                    continue;
                //若是沒有現成的,也沒有創造中的,那就開始創造代理類
                } else {
                    /*
                     * No proxy class for this list of interfaces has been
                     * generated or is being generated, so we will go and
                     * generate it now. Mark it as pending generation.
                     */
                    //將當前代理類置爲正在構造中,並直接退出循環
                    cache.put(key, pendingGenerationMarker);
                    break;
                }
            } while (true);
        }

        try {
            String proxyPkg = null; // package to define proxy class in

            //這一段是看你傳入的接口中有沒有不是public的接口,若是有,這些接口必須所有在一個包裏定義的,不然拋異常
            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package. Verify that all
             * non-public proxy interfaces are in the same package.
             */
            for (int i = 0; i < interfaces.length; i++) {
                int flags = interfaces[i].getModifiers();
                if (!Modifier.isPublic(flags)) {
                    String name = interfaces[i].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) { // if no non-public proxy interfaces,
                proxyPkg = ""; // use the unnamed package
            }

            {
                /*
                 * Choose a name for the proxy class to generate.
                 */
                long num;
                synchronized (nextUniqueNumberLock) {
                    num = nextUniqueNumber++;
                }
                //生成一個隨機代理類名
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
                /*
                 * Verify that the class loader hasn't already defined a class
                 * with the chosen name.
                 */
                
                //這一句就是重中之重了,生成代理類的class文件,這就是JDK動態代理的原理了,經過動態生成class文件來產生的代理類
                //這個generateProxyClass方法下面會着重介紹
                /*
                 * Generate the specified proxy class.
                 */
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                        proxyName, interfaces);
                try {
                    //獲得class文件二進制流後,直接載入代理類
                    proxyClass = 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());
                }
            }
            //proxyClasses這個Map是爲了來判斷是否是代理的Class
            // add to set of all generated proxy classes, for isProxyClass
            proxyClasses.put(proxyClass, null);

        } finally {
            /*
             * We must clean up the "pending generation" state of the proxy
             * class cache entry somehow. If a proxy class was successfully
             * generated, store it in the cache (with a weak reference);
             * otherwise, remove the reserved entry. In all cases, notify all
             * waiters on reserved entries in this cache.
             */
            synchronized (cache) {
                if (proxyClass != null) {
                    //最終將生成的代理用弱引用包裝起來放到cache當中
                    cache.put(key, new WeakReference(proxyClass));
                } else {
                    //若是代理類是空則移除代理的接口所表明的key值
                    cache.remove(key);
                }
                //通知正在等待在cache對象上的線程,告訴他們能夠繼續了,代理Class加載完畢了
                cache.notifyAll();
            }
        }
        return proxyClass;
    }
複製代碼

                上面基本上已經解釋的很清楚了,下面就是去看一下byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces)這句話是如何處理的,也就是如何獲得的代理類的class文件的,咱們進去源碼看一下,我依然會加上註釋。

複製代碼
public static byte[] generateProxyClass(String paramString, Class[] paramArrayOfClass) {
        //新建一個ProxyGenerator實例,傳入類名和接口數組
        ProxyGenerator localProxyGenerator = new ProxyGenerator(paramString, paramArrayOfClass);
        //這個纔是真正生成class文件的地方
        final byte[] arrayOfByte = localProxyGenerator.generateClassFile();
        //看保存生成文件的標誌是否爲真,若是是就將class文件生成到本地,生成時要檢查權限
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction() {
                public Object run() {
                    try {
                        FileOutputStream localFileOutputStream = new FileOutputStream( ProxyGenerator.dotToSlash(this.val$name) + ".class");
                        localFileOutputStream.write(arrayOfByte);
                        localFileOutputStream.close();
                        return null;
                    } catch (IOException localIOException) {
                        throw new InternalError( "I/O exception saving generated file: " + localIOException);
                    }
                }

            });
        }

        return arrayOfByte;
    }
複製代碼

                咱們繼續跟蹤到localProxyGenerator.generateClassFile()這一句當中,依然會加上註釋。

複製代碼
private byte[] generateClassFile() {
        //addProxyMethod方法,就是將方法都加入到一個列表中,並與對應的class對應起來
        //這裏給Object對應了三個方法hashCode,toString和equals
        addProxyMethod(hashCodeMethod, Object.class);
        addProxyMethod(equalsMethod, Object.class);
        addProxyMethod(toStringMethod, Object.class);

        //一樣將接口與接口下的方法對應起來
        for (int i = 0; i < this.interfaces.length; i++) {
            localObject1 = this.interfaces[i].getMethods();
            for (int k = 0; k < localObject1.length; k++) {
                addProxyMethod(localObject1[k], this.interfaces[i]);
            }

        }
        //檢查全部代理方法的返回類型
        for (Iterator localIterator1 = this.proxyMethods.values().iterator(); localIterator1
                .hasNext();) {
            localObject1 = (List) localIterator1.next();
            checkReturnTypes((List) localObject1);
        }

        Object localObject2;
        try {
            //方法中加入構造方法,這個構造方法只有一個,就是一個帶有InvocationHandler接口的構造方法
            //這個纔是真正給class文件,也就是代理類加入方法了,不過還沒真正處理,只是先加進來等待循環,構造方法在class文件中的名稱描述是<init>
            this.methods.add(generateConstructor());
            //循環代理方法
            for (localIterator1 = this.proxyMethods.values().iterator(); localIterator1
                    .hasNext();) {
                localObject1 = (List) localIterator1.next();
                for (localIterator2 = ((List) localObject1).iterator(); localIterator2
                        .hasNext();) {
                    localObject2 = (ProxyMethod) localIterator2.next();
                    //給每個代理方法加一個Method類型的屬性,數字10是class文件的標識符,表明這些屬性都是private static的
                    this.fields.add(new FieldInfo(((ProxyMethod) localObject2).methodFieldName,"Ljava/lang/reflect/Method;", 10));
                    //將每個代理方法都加到代理類的方法中
                    this.methods.add(((ProxyMethod) localObject2).generateMethod());
                }
            }
            Iterator localIterator2;
            //加入一個靜態初始化塊,將每個屬性都初始化,這裏靜態代碼塊也叫類構造方法,其實就是名稱爲<clinit>的方法,因此加到方法列表
            this.methods.add(generateStaticInitializer());
        } catch (IOException localIOException1) {
            throw new InternalError("unexpected I/O Exception");
        }
        //方法和屬性個數都不能超過65535,包括剛纔的接口個數也是這樣,
        //這是由於在class文件中,這些個數都是用4位16進製表示的,因此最大值是2的16次方-1
        if (this.methods.size() > 65535) {
            throw new IllegalArgumentException("method limit exceeded");
        }
        if (this.fields.size() > 65535) {
            throw new IllegalArgumentException("field limit exceeded");
        }
        //這裏是將類名中的.轉成成斜線爲了寫入class文件。
        this.cp.getClass(dotToSlash(this.className));
        this.cp.getClass("java/lang/reflect/Proxy");
        for (int j = 0; j < this.interfaces.length; j++) {
            this.cp.getClass(dotToSlash(this.interfaces[j].getName()));
        }

        this.cp.setReadOnly();
        //這裏開始真正的寫class文件
        ByteArrayOutputStream localByteArrayOutputStream = new ByteArrayOutputStream();
        Object localObject1 = new DataOutputStream(localByteArrayOutputStream);
        try {
            //寫入class文件的標識號,標識這是一個class文件
            ((DataOutputStream) localObject1).writeInt(-889275714);
            //次版本號0
            ((DataOutputStream) localObject1).writeShort(0);
            //主版本號,49表明的是JDK1.5
            ((DataOutputStream) localObject1).writeShort(49);
            //這裏寫入的是常量池,包括一些屬性名稱,類名稱,方法描述符,屬性描述符等等,常量池在加載時會被放到方法區或者說永久代。
            this.cp.write((OutputStream) localObject1);
            //這裏寫入的是這個類的訪問標識,49表明的是public final,也就是說JDK動態代理生成的代理類都是final的
            ((DataOutputStream) localObject1).writeShort(49);
            //寫入代理類的類名
            ((DataOutputStream) localObject1).writeShort(this.cp
                    .getClass(dotToSlash(this.className)));
            //寫入代理類的父類類名,也就是Proxy類,這個位置的類若是說是JAVA文件,至關於extend後面的類,也就是父類
            ((DataOutputStream) localObject1).writeShort(this.cp
                    .getClass("java/lang/reflect/Proxy"));
            //寫入代理類所實現的接口數量
            ((DataOutputStream) localObject1)
                    .writeShort(this.interfaces.length);
            //寫入代理類所實現的接口類名,一樣的,對於JAVA文件來講,至關於implements後面的接口,也就是實現的接口
            for (int m = 0; m < this.interfaces.length; m++) {
                ((DataOutputStream) localObject1).writeShort(this.cp
                        .getClass(dotToSlash(this.interfaces[m].getName())));
            }
            //寫入屬性個數
            ((DataOutputStream) localObject1).writeShort(this.fields.size());
            //寫入屬性描述
            for (Iterator localIterator3 = this.fields.iterator(); localIterator3
                    .hasNext();) {
                localObject2 = (FieldInfo) localIterator3.next();
                ((FieldInfo) localObject2)
                        .write((DataOutputStream) localObject1);
            }
            //寫入方法個數
            ((DataOutputStream) localObject1).writeShort(this.methods.size());
            //寫入方法描述,方法的code屬性,以及構造方法和類構造方法都在這裏被寫入了。
            for (localIterator3 = this.methods.iterator(); localIterator3
                    .hasNext();) {
                localObject2 = (MethodInfo) localIterator3.next();
                ((MethodInfo) localObject2)
                        .write((DataOutputStream) localObject1);
            }
            //結束
            ((DataOutputStream) localObject1).writeShort(0);
        } catch (IOException localIOException2) {
            throw new InternalError("unexpected I/O Exception");
        }

        return localByteArrayOutputStream.toByteArray();
    }
複製代碼

                其實代理類的class文件並不複雜,仍是有不少規律可循的,因此上述過程基本上可讓各位瞭解下JDK動態代理生成代理類時都生成了什麼東西。

                下面咱們能夠調用下JDK中生成Class文件的方法,而且寫入到本地文件,而後使用反編譯工具來看一下生成的代理類究竟是什麼樣子的。下面是生成文件的測試類。咱們暫且將生成的類名寫成TestProxy,代理的接口就是咱們上面的TestInterface。以下。

複製代碼
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import sun.misc.ProxyGenerator;



public class CreateClassTest {

    public static void main(String[] args) throws IOException {
        byte[] classFile = ProxyGenerator.generateProxyClass("TestProxy", new Class[]{TestInterface.class});
        File file = new File("F:/TestProxy.class");
        FileOutputStream fos = new FileOutputStream(file);
        fos.write(classFile);
        fos.flush();
        fos.close();
    }
    
}
複製代碼

                生成後,咱們反編譯過來會是以下格式的JAVA文件。我加入了註釋,大體說明了下文件中生成的部分與剛纔分析的時候寫入的過程的對應關係。

複製代碼
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

//public final的,繼承Proxy,實現你傳入的接口
public final class TestProxy extends Proxy
  implements TestInterface
{
  //private static 的Method屬性,對應全部方法
  private static Method m1;
  private static Method m5;
  private static Method m3;
  private static Method m4;
  private static Method m0;
  private static Method m2;
  //惟一的構造方法,須要一個InvocationHandler接口傳入
  public TestProxy(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }
  //重寫Object的三個方法
  public final boolean equals(Object paramObject)
    throws 
  {
    try
    {
      return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void method3()
    throws 
  {
    try
    {
      this.h.invoke(this, m5, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  //代理的三個方法,回調傳入的InvocationHandler的invoke方法
  public final void method1()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

  public final void method2()
    throws 
  {
    try
    {
      this.h.invoke(this, m4, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

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

  public final String toString()
    throws 
  {
    try
    {
      return (String)this.h.invoke(this, m2, null);
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }
  //這個就是剛纔this.methods.add(generateStaticInitializer());這一句話所加入的靜態初始化塊,初始化每個屬性
  static
  {
    try
    {//每個屬性所表明的Method都是與上面加入代理方法列表時與固定類綁定的,這是class文件中的格式,方法要與固定的類綁定
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m5 = Class.forName("TestInterface").getMethod("method3", new Class[0]);
      m3 = Class.forName("TestInterface").getMethod("method1", new Class[0]);
      m4 = Class.forName("TestInterface").getMethod("method2", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }
}
複製代碼

        看到這裏就知道invoke方法是幹嗎的了,其實就是生成的代理類對每個方法的處理就是回調invoke方法。從生成的代理類源文件中也能夠發現,每個Method除了hashCode,toString和equals外,都是與所屬的接口綁定的,因此這也就解釋了爲何咱們不實現這個接口,只傳入進入的話,不能直接使用method.invoke,而是要轉成source對應的method才能夠調用。

        好了,代理模式就分析到這裏了,這裏講的更多的是代理模式的原理,對於如何使用並無講述太多,是由於代理模式在平時工做中用的雖然不少,但咱們大可能是使用的現成的,緣由很簡單,就是由於spring的AOP已經給咱們弄了一個很好的動態代理的框架,因此咱們幾乎不須要本身去寫,只要明白其原理,知道動態代理和靜態代理主要處理的問題是那種的,知道在何處用,也可以用起來駕輕就熟就能夠了

轉載:https://www.cnblogs.com/zuoxiaolong/p/pattern4.html

相關文章
相關標籤/搜索