從Proxy.newInstance出發


寫在前面

本篇博客是基於對動態代理,java的重寫,多態特性了解的基礎上對於源碼的閱讀,先提出幾個問題
1.從靜態代理變成動態代理需要解決兩個問題,如何動態實現被代理類的接口並摘取接口中的方法,如果動態的為被代理方法添加附加邏輯
2.給出一個例子:

interface a {
	String get() throws Exception;
}

interface b {
	Object get() throws NullPointerException;
 }
}

如果我要代理即實現了a又實現了b的類的get方法,那么在代理類$Proxy0中基於a和b的get方法動態生成的get方法的描述是什么?怎么實現?
3.能否舉出代理失敗的例子?(兩種?)
閱讀建議:涉及的方法以及域比較多,前后是連貫的,抓住關聯

重要的域

先從proxy類開始

//一個靜態常量,后面會用到
private static final Class<?>[] constructorParams = { InvocationHandler.class };

//代理類的緩存
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

//InvocationHandler類型變量,注意它的訪問權限為protected,也就是說子類可以為它賦值
protected InvocationHandler h;

newProxyInstance

/**
*loader:常常將被代理類的classloader傳入
*interfaces:被代理類的接口集合
*h:代理輔助類,被代理方法的實際調用者
**/
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        //檢查h是否為空,如果為空則拋出NullPointerException
        Objects.requireNonNull(h);
        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }
        //生成代理類
        Class<?> cl = getProxyClass0(loader, intfs);
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
            //反射獲得代理類的構造函數,其中傳入參數constructorParams是前面定義的靜態常量,這也間接說明代理類的共同特點:g構造函數傳入參數為InvocationHandler類型
            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);
        }
    }

getProxyClass0

 	private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }
       	//官方解釋:如果在緩存中找到匹配loader和interfaces的代理類則直接返回,否則通過ProxyClassFactory新建一個
        return proxyClassCache.get(loader, interfaces);
    }

proxyClassCache在上述域中已經說明,它是屬於WeakCache類型,傳入了KeyFactory和ProxyClassFactory的實例作為參數進行了實例化,下面我們來看下get方法

get

 public V get(K key, P parameter) {
 		//確保interfaces不為空
        Objects.requireNonNull(parameter);
        //在這個方法中有很多緩存的操作,不懂,省略處理,以后填坑
        expungeStaleEntries();
        //省略
        while (true) {
            if (supplier != null) {
                //這句和代理類的生成有關
                V value = supplier.get();
        //省略
    }

上述注釋的supplier.get()實現另一個方法的調用,在weakCache中有一個Factory靜態內部類,它實現了supplier接口,因此supplier調用get實際是調用其子類Factory對象的get方法

get

public synchronized V get() { 
			//緩存操作不懂省略
            Supplier<V> supplier = valuesMap.get(subKey);
            if (supplier != this) {
                return null;
            }
            V value = null;
            try {
            	//很明顯可以發現下面的valueFactory.apply是用來生成代理類的
                value = Objects.requireNonNull(valueFactory.apply(key, parameter));
            } 
            //省略
        }
    }

valueFactory是上述在實例化weakCache時傳入的ProxyClassFactory的對象的引用,下面查看它的apply方法

apply

 public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
 			//用來判斷interfaces中是否重復的set
            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                Class<?> interfaceClass = null;
                try {
                	//用我們傳入的classloader加載interface
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                //如果上述加載的interfaceclass和我們直接傳入的intf不同,則說明傳入的loader和interface不匹配,拋出異常
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                //判斷是否為接口
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                //判斷interfaces是否有重復
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }
            //代理類的package值
            String proxyPkg = null;
            //代理類的訪問權限 
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
            //遍歷判斷interfaces中是否有非public訪問權限的interface,如果有的話,代理類所在的包必須和它一致
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                //如果不是公有權限
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    //下面三行代碼用來獲得intf所在包,例如若intf = InvacationHandler.class,pkg將等於java.lang.reflect.
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    //如果有多個非public的interface並且它們不在同一個包中,將拋出異常
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }
            //如果所有的interfaces都為public,那么代理類所在的包為com.sun.proxy.
            if (proxyPkg == null) {
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }
            /**下面兩行代碼用到兩個ProxyClassFactory中的域,分別是 :     
            /*1.private static final String proxyClassNamePrefix = "$Proxy";
            /*2.private static final AtomicLong nextUniqueNumber = new AtomicLong();
			**/
            long num = nextUniqueNumber.getAndIncrement();
            //例如proxyName為com.sun.proxy.$proxy0
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
            //核心邏輯的調用出現了
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
            	//defineClass0為本地方法,用來動態加載class文件
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

經過數次跳轉,終於來到了核心代碼地,即ProxyGenerator的generateProxyClass方法,sun的jdk中該部分代碼不是開源,可以參考openJdk,接下來僅貼出主要代碼

generateProxyClass

public static byte[] generateProxyClass(final String name, Class[] interfaces)  
 {
        ProxyGenerator gen = new ProxyGenerator(name, interfaces);
        final byte[] classFile = gen.generateClassFile();
        //中間省略
        return classFile;
    }

在介紹ProxyGenerator的其他方法之前先來看一下其中的一些域

/**一個用來過渡處理(怎么處理?)所有method對象的hashmap,String的組成是Method對象的name加上參數列表(為什么這樣標識),對於代理類而言每一個String標識對應的List中必須只有一個元素,否則方法就重復了**/
private Map<String, List<ProxyMethod>> proxyMethods = new HashMap<String,List<ProxyMethod>>();
//包含代理類所有method信息的集合
private List<MethodInfo> methods = new ArrayList<>();
//包含代理類所有field信息的集合
private List<FieldInfo> fields = new ArrayList<>();

generateClassFile

private byte[] generateClassFile() {
 		//step1:針對所有接口中的方法生成Method對象
        
        //首先向proxyMethods中添加Object的hashcode,equals,toString三個method對象
         addProxyMethod(hashCodeMethod, Object.class);
         addProxyMethod(equalsMethod, Object.class);
         addProxyMethod(toStringMethod, Object.class);
 
        //然后添加接口中的method對象
         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]);
             }
         }
         //對每一個String標識的List中的所有ProxyMethod的返回值類型進行兼容性處理(涉及到重寫特性)(為什么要處理?怎么處理)
         for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
             checkReturnTypes(sigmethods);
         }
 
         //step2:向methods和fields中分別添加methodInfo和FieldInfo
         try {
         	//添加構造函數
             methods.add(generateConstructor());
             for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
                 for (ProxyMethod pm : sigmethods) {
                 	//對於每個method對象添加靜態引用
                     fields.add(new FieldInfo(pm.methodFieldName,
                         "Ljava/lang/reflect/Method;",
                          ACC_PRIVATE | ACC_STATIC));
 
                     //添加在proxyMethods這個hashMap中走過一遭的的ProxyMethod的methodInfo
                     methods.add(pm.generateMethod());
                 }
             }
 			//添加靜態初始化方法,例如:<clinit>,類在被加載的時候,為所有靜態初始化的變量生成一個clinit方法,然后在里面執行賦值
             methods.add(generateStaticInitializer());
        //step3:漫長的拼class文件系列,不再展示
 

接下來是解釋前面提出的過渡處理以及開頭提出的問題2

addProxyMethod

//該方法一方面是向proxyMethods這個hashmap中添加不同標識的method對象,另一方面是針對相同的標識進行兼容性處理
private void addProxyMethod(Method m, Class fromClass) {
		//反射獲得method的name,parameterTypes,returnType,exceptionTypes
         String name = m.getName();
         Class[] parameterTypes = m.getParameterTypes();
         Class returnType = m.getReturnType();
         Class[] exceptionTypes = m.getExceptionTypes();
 		//形成該m的sig(標識)即name+參數列表
         String sig = name + getParameterDescriptors(parameterTypes);
        //proxyMethods是上述提到的hashMap(key為sig)
         List<ProxyMethod> sigmethods = proxyMethods.get(sig);
         //如果在proxyMethods中能找到和m標識一樣的ProxyMethod,例如:開頭問題2中a和b的標識就是一樣的
         if (sigmethods != null) {
             for (ProxyMethod pm : sigmethods) {
             	//並且返回類型相同
                 if (returnType == pm.returnType) {
                 	//兼容性處理:將pm的異常大小要小於等於pm和m中的最小值(重寫特性),不需要向proxyMethods中添加m了
                     List<Class> legalExceptions = new ArrayList<Class>();
                     collectCompatibleTypes(
                         exceptionTypes, pm.exceptionTypes, legalExceptions);
                     collectCompatibleTypes(
                         pm.exceptionTypes, exceptionTypes, legalExceptions);
                     pm.exceptionTypes = new Class[legalExceptions.size()];
                     pm.exceptionTypes =
                         legalExceptions.toArray(pm.exceptionTypes);
                     return;
                 }
             }
         }
         //如果沒有找到相同的標識,proxyMethods添加一個新的key-value鍵值對,value是一個list初始容量為3
         else {
             sigmethods = new ArrayList<ProxyMethod>(3);
             proxyMethods.put(sig, sigmethods);
         }
         //無論是在proxyMethods中與m有相同標識但是返回類型不同,還是沒有相同標識,都要向proxyMethods對應key的list中添加m
         sigmethods.add(new ProxyMethod(name, parameterTypes, returnType,
                                        exceptionTypes, fromClass));
     }

經歷該方法后,proxyMethods中某個key的list的ProxyMethod個數可能大於1(如果直接用來生成代理類,將會有重復的方法),這些ProxyMethod的name和參數列表相同,返回類型不同,接下來checkReturnTypes處理

checkReturnTypes

在前面是通過

  for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
      checkReturnTypes(sigmethods);
  }

這段代碼來進入checkReturnTypes方法的

/**checkReturnTypes方法是專門進行兼容性處理,對於需要實現的接口中有重復的方法(如前面問題2的a,b),為了對實現這些接口的類都進行代理,代理類必須生成一個同時對它們進行重寫了的方法,也就是說動態生成的這個方法,在每個接口看來都是對它內部抽象方法的重寫,真相就是:代理類方法的返回類型必須是interfaces中所有重復方法返回類型的中的一個,並且這個返回類型是其他重復方法返回類型的子類(例如:參照開頭interface a,b,代理類的get方法的返回類型是String)**/
 static void checkReturnTypes(List<ProxyMethod> methods) {
 	   //如果proxyMethods中該標識的list元素小於2,那就不需要處理了
       if (methods.size() < 2) {
           return;
       }
       //用來記錄已經處理的所有返回類型中,已經兼容其它返回類型的返回類型(例如:參照開頭interface a,b,在處理了它們的重復get方法之后,代理類方法返回類型為String)
       LinkedList<Class<?>> uncoveredReturnTypes = new LinkedList<>();
   //continue的跳轉標簽
   nextNewReturnType:
       for (ProxyMethod pm : methods) {
           Class<?> newReturnType = pm.returnType;
           //如果某個Proxymethod的返回類型是基本類型(int,char,long,double等),就直接拋出異常,因為基本類型和其它返回類型都不兼容
           if (newReturnType.isPrimitive()) {
               throw new IllegalArgumentException(
                   "methods with same signature " +
                   getFriendlyMethodSignature(pm.methodName,
                                              pm.parameterTypes) +
                   " but incompatible return types: " +
                   newReturnType.getName() + " and others");
           }
           boolean added = false;
           //遍歷uncoveredReturnTypes
           ListIterator<Class<?>> liter = uncoveredReturnTypes.listIterator();
           while (liter.hasNext()) {
               Class<?> uncoveredReturnType = liter.next();
               //如果pm的返回類型是uncoveredReturnType的父類,那么uncoveredReturnType也兼容pm,直接continue
               if (newReturnType.isAssignableFrom(uncoveredReturnType)) {
                   assert !added;
                   continue nextNewReturnType;
               }
               //如果pm的返回類型是uncoveredReturnType的子類,那么pm代替uncoveredReturnType兼容其它已經處理的returntype
               if (uncoveredReturnType.isAssignableFrom(newReturnType)) {
                   if (!added) {
                       liter.set(newReturnType);
                       added = true;
                   } 
                   //uncoveredReturnType中可能也有多個returnType,它們之間不兼容,當下面分支執行的時候,就說明pm的返回類型同時兼容uncoveredReturnType中的某些returnType,那么在uncoveredReturnType中這些returnType就可以移除了
                   else {
                       liter.remove();
                   }
               }
           }
           //向uncoveredReturnTypes中添加不兼容的類,
           if (!added) {
               uncoveredReturnTypes.add(newReturnType);
           }
       }
       //如果uncoveredReturnTypes中返回類型多於1個,也就是說:不存在一個返回類型兼容其它所有的,那么代理類中對於該方法也就無法重寫了,拋出異常
       if (uncoveredReturnTypes.size() > 1) {
           ProxyMethod pm = methods.get(0);
           throw new IllegalArgumentException(
               "methods with same signature " +
               getFriendlyMethodSignature(pm.methodName, pm.parameterTypes) +
               " but incompatible return types: " + uncoveredReturnTypes);
       }
   }

表述的比較拗口,下圖展示checkReturnTypes方法

最后LinedList就是兼容所有類型的類

$proxy0代碼

public final class $Proxy0 extends Proxy implements a,b{  
//根據方法生成的靜態的域 
private static Method m1;  
private static Method m0;  
private static Method m3;  
private static Method m2;  
 
//類加載時,會在clinit方法中進行初始化 
static {  
   try {  
    m1 = Class.forName("java.lang.Object").getMethod("equals",  
      new Class[] { Class.forName("java.lang.Object") });  
    m0 = Class.forName("java.lang.Object").getMethod("hashCode",  
      new Class[0]);  
    m3 = Class.forName("my.java.reflect").getMethod("get",  
      new Class[0]);  
    m2 = Class.forName("java.lang.Object").getMethod("toString",  
      new Class[0]);  
   } catch (NoSuchMethodException nosuchmethodexception) {  
    throw new NoSuchMethodError(nosuchmethodexception.getMessage());  
   } catch (ClassNotFoundException classnotfoundexception) {  
    throw new NoClassDefFoundError(classnotfoundexception.getMessage());  
   }  
}  
//還記得Proxy類中InvocationHandler h的訪問權限為protected嗎?這個時候賦值
public $Proxy0(InvocationHandler invocationhandler) {  
   super(invocationhandler);  
}  
  
@Override  
public final boolean equals(Object obj) {  
   try {  
    return ((Boolean) super.h.invoke(this, m1, new Object[] { obj }))  
      .booleanValue();  
   } catch (Throwable throwable) {  
    throw new UndeclaredThrowableException(throwable);  
   }  
}  
  
@Override  
public final int hashCode() {  
   try {  
    return ((Integer) super.h.invoke(this, m0, null)).intValue();  
   } catch (Throwable throwable) {  
    throw new UndeclaredThrowableException(throwable);  
   }  
}  
  
public final String get() {  
   try {  
    return (String)super.h.invoke(this, m3, null);  
   } catch (Error e) {  
   } catch (Throwable throwable) {  
    throw new UndeclaredThrowableException(throwable);  
   }  
}  
  
@Override  
public final String toString() {  
   try {  
    return (String) super.h.invoke(this, m2, null);  
   } catch (Throwable throwable) {  
    throw new UndeclaredThrowableException(throwable);  
   }  
}  
}  

以上


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM