1. 程式人生 > >Deep learning about Java--貫穿Java的反射機制(3)

Deep learning about Java--貫穿Java的反射機制(3)

1.代理模式的定義
給委託物件提供一個代理物件,並由代理物件控制對於委託物件的訪問,也就是說客戶不能夠直接操作委託物件,而是通過代理物件間接地操作委託物件。
簡單地說就是:通過代理物件對委託物件的相關方法的呼叫來提供特定的服務。
代理物件一般主要負責的行為有:
為委託物件預處理訊息、過濾訊息、轉發訊息給委託物件(代理物件對於委託物件的呼叫/控制)以及事後處理訊息。

再來,以經典的代理模式的例子來說明(引用計數–reference counting)代理:
當需要一個複雜物件的多份副本時,代理模式可以結合享元模式以減少儲存器的使用量。
典型的做法時建立一個複雜物件以及多個代理,每個代理都會引用原本的複雜物件,在代理產生操作資料時會把資料傳輸到原本的複雜物件。如果所有的代理都不存在時,複雜物件也沒有存在的意義了,它將會被移除。

現實中代理的例子:
譬如,我們搭個梯子來越過GFW來訪問海外網站,這個遠端代理的過程是這樣的:
(1)使用者把Http請求傳送給代理
(2)代理把Http請求傳送給伺服器
(3)伺服器把Http響應傳送給代理
(4)代理把Http響應傳送給使用者
這裡寫圖片描述

2.代理模式的實現思路
代理類和目標類都需要實現同一個介面。
代理類和目標類分別實現介面的具體邏輯。
在代理類的建構函式中例項化一個目標類的物件。
在代理類中呼叫目標類中實現的介面邏輯。
客戶如果需要訪問或呼叫目標類的介面邏輯只能通過代理來進行。

3.靜態代理和動態代理(JDK、cglib)
靜態代理:
即不通過反射的方式,而是通過直接對委託類的初始化和方法訪問進行控制。在所有的原始碼進過編譯之後,所有的類都會產生.class檔案,此時,我們為了獲取更好的載入效能,一開始並不是就對所有的類進行載入,而是選擇載入他們的代理類。這樣的做法的優勢是 加快系統的啟動速度,減少使用者的等待時間。
下面給出一個能很好地解釋靜態代理設計模式–懶漢模式(延遲載入,即使用時才會載入)

public interface MyInterface{
    //public abstract是可以不用寫的,因為在介面中預設的Modifier就是public abstract
    public abstract void method();
    public abstract void method2();
}
public class ConsignorSubject implements MyInterface{

    public ConsignorSubject(){}

    @Override
    public void method(){
        System.out.println("hello method"
); } @Override public void method2(){ System.out.println("hello method2"); } }
public class StaticProxy implements MyInterface{
    //指定代理物件
    private ConsignorSubject consignor = null;
    //載入的時候主要物件是對代理類
    //需要使用new進行代理類的初始化
    public StaticProxy(){}

    //在靜態代理中,代理類和委託類都必須實現統一的介面
    @Override
    public void method(){
        if(null == consignor)
            consignor = new ConsignorSubject();
        //前置訊息的過濾、修飾
        System.out.println("Information filtering and decorating");
        //訊息轉發給委託類、控制委託類處理訊息
        consignor.method();
        //後置訊息的清除
         System.out.println("After!")
    }

     @Override
    public void method2(){
        Sif(null == consignor)
            consignor = new ConsignorSubject();
        //前置訊息的過濾、修飾
        System.out.println("Information filtering and decorating");
        //訊息轉發給委託類、控制委託類處理訊息
        consignor.method2();
        //後置訊息的清除
         System.out.println("After!")
    }
}
public class Demo{
    public static void main(String[] args){
        StaticProxy sp = new StaticProxy();
        sp.method();
        sp.method2();
    }
}

動態代理:
在程式執行時,通過反射機制建立生成;動態代理Proxy是一個靜態類,它已經是在java.lang.reflect.Proxy中封裝好的,不需要我們再去編寫它的具體實現,只需要傳入相應的引數即可通過反射來代理委託類。
Proxy類提供了建立動態代理類及其例項的靜態方法。
(1)getProxyClass0()靜態方法負責建立動態代理類,它的完整定義如下:

public static Class<?> getProxyClass0(ClassLoader loader,Class<?>[] interfaces) throwsIllegalArgumentException

引數loader 指定動態代理類的類載入器,引數interfaces指定動態代理類需要實現的所有介面。

(2)newProxyInstance()靜態方法負責建立動態代理類的例項,它的完整定義如下:

public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler handler) throws
     IllegalArgumentException

引數loader指定動態代理類的類載入器,引數interfaces 指定動態代理類需要實現的所有介面,引數handler 指定與動態代理類關聯的InvocationHandler 物件。
下面我們來看一個例項:這裡寫圖片描述

package com.unicorn.reflect.pojo;

public interface Person {
    public void testMethod();

    public void testMethod2();

    public int testMethod3(String str);
}
package com.unicorn.reflect.pojo;

import java.io.Serializable;

import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.NonNull;
import lombok.Setter;
import lombok.ToString;

@NoArgsConstructor/**JavaBean規範:實現無參構造器*/
@ToString(exclude={"id"})/**JavaBean規範:過載tostring方法*/
@EqualsAndHashCode
public class Emp implements Serializable, Person{

    /**
     * JavaBean規範:實現序列化介面
     */
    private static final long serialVersionUID = -720655243074260608L;

    /**
     * JavaBean規範:實現getter and setter
     * 在這裡使用lombok的註解簡化冗餘的程式碼
     */
    @Getter @Setter private Long id;
    @Getter @Setter private String empName;
    @Getter @Setter private String depart;
    @Getter @Setter private Double salary;
    @Getter @Setter public Byte Sex;
    public static final int STABLE_VAL = 1;

    public Emp(@NonNull Long id){
        this.id = id;
    }

    @Override
    public void testMethod() {
        // TODO Auto-generated method stub
        System.out.println("method1 says'I wanna to do somthing crazy!'");
    }

    @Override
    public void testMethod2() {
        // TODO Auto-generated method stub
        System.out.println("method2 says'I wanna to kick the method1 for its crazy behaviors!'");
    }

    @Override
    public int testMethod3(String str) {
        // TODO Auto-generated method stub
        System.out.println("method2 says'The crazy world! Are you fxxxing kiding me ?" + str + "'");
        return 0;
    }

//  @Override public boolean equals(Object o){
//      if(o == this)
//          return true;
//      if(!(o instanceof Emp))
//          return false;
//  }
}
package com.unicorn.reflect.service;

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

import lombok.NoArgsConstructor;
import lombok.NonNull;

/**
 * 反射中最重要的動態代理
 * @author Unicorn
 *
 */
@NoArgsConstructor
public class TestInvocationHandler implements InvocationHandler {

    private Object tar;

    public TestInvocationHandler(@NonNull Object tar){
        this.tar = tar;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // TODO Auto-generated method stub
        System.out.println("Look!");
        Object result = method.invoke(tar, args);
        System.out.println("Bye!");
        return result;
    }

}
package com.unicorn.reflect.service;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;

import org.junit.Test;

import com.unicorn.reflect.pojo.Emp;
import com.unicorn.reflect.pojo.Person;

public class TestProxy {

    @Test
    public void testFunc() throws Throwable{
        /**
         * 原本,正常的emp實現的介面功能是這樣的
         */
        Person e = new Emp(666L);
        e.testMethod();
        e.testMethod2();
        e.testMethod3("shawn");

        System.out.println("---------------------");
        /**
         * 使用代理(proxy)之後,可以增添新的功能,相當於裝飾,而不需要去改變原本的類
         */
        TestInvocationHandler t = new TestInvocationHandler(e);
        Person eProxy = (Person)Proxy.newProxyInstance(e.getClass().getClassLoader(), 
                e.getClass().getInterfaces(), t);
        eProxy.testMethod();
        eProxy.testMethod2();
        eProxy.testMethod3("shawn");
    }

}

這裡寫圖片描述
這個例子是使用了JDK提供的Proxy代理方式,它的特點就是面向介面程式設計,它所能代理的委託類一定是一個介面,非介面類是不能夠通過Proxy代理的,只能通過cglib來進行純粹的類代理。
使用Proxy是不需要實現介面類的,但是需要傳入InvocationHandler的實現類,前兩個引數是傳遞給getProxyClass0來建立動態代理類。
我們現在來看看原始碼關於proxy物件的建立流程:

/**
     * Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.
     *
     * <p>{@code Proxy.newProxyInstance} throws
     * {@code IllegalArgumentException} for the same reasons that
     * {@code Proxy.getProxyClass} does.
     *
     * @param   loader the class loader to define the proxy class
     * @param   interfaces the list of interfaces for the proxy class
     *          to implement
     * @param   h the invocation handler to dispatch method invocations to
     * @return  a proxy instance with the specified invocation handler of a
     *          proxy class that is defined by the specified class loader
     *          and that implements the specified interfaces
     * @throws  IllegalArgumentException if any of the restrictions on the
     *          parameters that may be passed to {@code getProxyClass}
     *          are violated
     * @throws  SecurityException if a security manager, <em>s</em>, is present
     *          and any of the following conditions is met:
     *          <ul>
     *          <li> the given {@code loader} is {@code null} and
     *               the caller's class loader is not {@code null} and the
     *               invocation of {@link SecurityManager#checkPermission
     *               s.checkPermission} with
     *               {@code RuntimePermission("getClassLoader")} permission
     *               denies access;</li>
     *          <li> for each proxy interface, {@code intf},
     *               the caller's class loader is not the same as or an
     *               ancestor of the class loader for {@code intf} and
     *               invocation of {@link SecurityManager#checkPackageAccess
     *               s.checkPackageAccess()} denies access to {@code intf};</li>
     *          <li> any of the given proxy interfaces is non-public and the
     *               caller class is not in the same {@linkplain Package runtime package}
     *               as the non-public interface and the invocation of
     *               {@link SecurityManager#checkPermission s.checkPermission} with
     *               {@code ReflectPermission("newProxyInPackage.{package name}")}
     *               permission denies access.</li>
     *          </ul>
     * @throws  NullPointerException if the {@code interfaces} array
     *          argument or any of its elements are {@code null}, or
     *          if the invocation handler, {@code h}, is
     *          {@code null}
     */
    @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<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            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);
        }
    }
/**
     * Generate a proxy class.  Must call the checkProxyAccess method
     * to perform permission checks before calling this.
     */
    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing --->loader
        // the given interfaces exists, this will simply return the cached copy; --->interfaces
        // otherwise, it will create the proxy class via the ProxyClassFactory
        /**
         *這段英文說的是,如果loader已經定義過和interfaces已經存在(也就是說proxy已經至少實現過一次),就會從快取中直接拿proxy副本作為proxy;否則就通過ProxyClassFactory生成一個
         */
        return proxyClassCache.get(loader, interfaces);
    }
/**
     * a cache of proxy classes
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
 /**
     * Look-up the value through the cache. This always evaluates the
     * {@code subKeyFactory} function and optionally evaluates
     * {@code valueFactory} function if there is no entry in the cache for given
     * pair of (key, subKey) or the entry has already been cleared.
     *
     * @param key       possibly null key
     * @param parameter parameter used together with key to create sub-key and
     *                  value (should not be null)
     * @return the cached value (never null)
     * @throws NullPointerException if {@code parameter} passed in or
     *                              {@code sub-key} calculated by
     *                              {@code subKeyFactory} or {@code value}
     *                              calculated by {@code valueFactory} is null.
     */
    public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);
        //清理持有弱引用的WeakHashMap這種資料結構,一般用於快取
        expungeStaleEntries();
        //從佇列中獲取cacheKey
        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        //利用懶載入的方式填充Supplier, ConcurrentMap是一種執行緒安全的map
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;
//while迴圈的作用就是不停的獲取實現InvokeHandler的類, 這個類可以是從快取中拿到,也可是是從proxyFactoryClass生成的
        while (true) {
            if (supplier != null) {
                // supplier might be a Factory or a CacheValue<V> instance
                //supplier可能是一個工廠或者是CacheValue<V>的例項物件
                V value = supplier.get();
                if (value != null) {
                    return value;
                }
            }
            // else no supplier in cache
            // or a supplier that returned null (could be a cleared CacheValue
            // or a Factory that wasn't successful in installing the CacheValue)

            // lazily construct a Factory
            if (factory == null) {
                factory = new Factory(key, parameter, subKey, valuesMap);
            }

            if (supplier == null) {
                supplier = valuesMap.putIfAbsent(subKey, factory);
                if (supplier == null) {
                    // successfully installed Factory
                    supplier = factory;
                }
                // else retry with winning supplier
            } else {
                if (valuesMap.replace(subKey, supplier, factory)) {
                    // successfully replaced
                    // cleared CacheEntry / unsuccessful Factory
                    // with our Factory
                    supplier = factory;
                } else {
                    // retry with current supplier
                    supplier = valuesMap.get(subKey);
                }
            }
        }
    }

proxyClassCache物件中傳入new ProxyClassFactory()
中的apply方法才是建立代理的方法

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

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                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");
                }
                /*
                 * 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.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
//到此為止都是對介面的必要檢查
            /*
             * 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 (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) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
//產生代理的隨機名稱,如果日後除錯看到了$Proxy+數字的exception那麼很可能就是你的代理出現問題了

            /*
             * Generate the specified proxy class.
             */
             //以上通過之後,就會真正產生代理啦
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            //這個generateProxyClass才是核心,apply只是呼叫了它產生代理
            try {
                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());
            }
        }
    }
private byte[] generateClassFile() {
   /*
    * Step 1: Assemble ProxyMethod objects for all methods to
    * generate proxy dispatching code for.
    */
    //addProxyMethod方法,就是將方法都加入到一個列表中,並與對應的class對應起來 
   //這裡給Object對應了三個方法hashCode,toString和equals 
   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 each set of proxy methods with the same signature,
    * verify that the methods' return types are compatible.
    */
   for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
     checkReturnTypes(sigmethods);
   }
   /*
    * Step 2: Assemble FieldInfo and MethodInfo structs for all of
    * fields and methods in the class we are generating.
    */
    //方法中加入構造方法,這個構造方法只有一個,就是一個帶有InvocationHandler介面的構造方法 
    //這個才是真正給class檔案,也就是代理類加入方法了,不過還沒真正處理,只是先加進來等待迴圈,構造方法在class檔案中的名稱描述是<init> 
 try {
   methods.add(generateConstructor());
   for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
     for (ProxyMethod pm : sigmethods) {
//給每一個代理方法加一個Method型別的屬性,數字10是class檔案的識別符號,代表這些屬性都是private static的 
       fields.add(new FieldInfo(pm.methodFieldName,
         "Ljava/lang/reflect/Method;",
          ACC_PRIVATE | ACC_STATIC));
       //將每一個代理方法都加到代理類的方法中 
       methods.add(pm.generateMethod());
     }
   }
 //加入一個靜態初始化塊,將每一個屬性都初始化,這裡靜態程式碼塊也叫類構造方法,其實就是名稱為<clinit>的方法,所以加到方法列表 
     methods.add(generateStaticInitializer());
   } catch (IOException e) {
     throw new InternalError("unexpected I/O Exception");
   }
 //方法和屬性個數都不能超過65535,包括之前的介面個數也是這樣, 
 //這是因為在class檔案中,這些個數都是用4位16進製表示的,所以最大值是2的16次方-1 
   if (methods.size() > 65535) {
     throw new IllegalArgumentException("method limit exceeded");
   }
   if (fields.size() > 65535) {
     throw new IllegalArgumentException("field limit exceeded");
   }
 //接下來就是寫class檔案的過程, 包括模數, 類名,常量池等一系列位元組碼的組成,就不一一細說了。需要的可以參考JVM虛擬機器位元組碼的相關知識。
   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 {
                   // 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();
 }

到此就結束了!cglib的筆者比較少用,暫時就不寫先了!
感謝pastqing (原始碼分析的思路是參照他的,筆者很服氣這個博主,給個贊!)還有這個博主陳善亮-BUPT(寫的沒前面的博主好,但好在有一些理解是很值得借鑑的)。
轉載請註明出處,謝謝!