Java反射和代理簡介

本文所引用的代碼爲JDK 1.8版本

請尊重博主勞動成果,轉載請標明原文鏈接。

反射

Java反射中最常使用到的幾個類:Class,Constructor,Method,Field。

  • Class:用於獲取類的字節碼對象,獲取構造方法,普通方法,以及屬性。
  • Constructor;用於創建對象。
  • Method:調用對象的方法和調用類的靜態方法。獲取方法上配置的註解信息。
  • Field:修改對象的屬性或類的靜態屬性,獲取對象或類的屬性值。獲取屬性上配置的註解信息。

在某些被限定不能訪問的類,方法或屬性,當我需要創建對象,調用方法或獲取值時,則可以使用反射來繞過這些非public修飾符的限制。

Class

常用到的幾個方法:

獲取字節碼對象

forName(String className)

該方法是靜態方法,用於獲取類的字節碼對象,className爲類的路徑。使用方式

Class<?> clazz = Class.forName("xx.xx.Xxx");

’xx.xx.Xxx‘是類的路徑。

獲取構造器

getConstructors()

獲取字節碼對象中所有public類型的構造方法,返回值爲Constructor

public A(int i, String s){
    ...
}

則獲取A的該構造方法的方式(clazz爲類A的字節碼對象)

Constructor<?> con = clazzA.getConstructor(int.class, String.class);

getDeclaredMethods()
獲取字節碼對象中所有類型的構造方法,返回值爲Constructor

獲取方法

getMethods()

獲取字節碼對象中public類型的方法,包含public類型的靜態方法,返回值爲Method[]。

getDeclaredConstructors()

獲取字節碼對象中所有類型的方法,返回值爲Method[]。

getMethod(String name, Class

獲取屬性

getFields()

獲取字節碼對象中public類型的屬性,包含public類型的靜態屬性,返回值爲Field[]。

getDeclaredFields()

獲取字節碼對象中所有類型的屬性,返回值爲Field[]。

getField(String name)

獲取指定名稱的public屬性,返回值爲Field。

getDeclaredField(String name)
獲取指定名稱的屬性,private,protected和缺省類型的都能夠獲取,返回值爲Field。

小結

  • 獲取所有public類型的構造器,方法或屬性的方式:getXxxs(),返回數組。
  • 獲取所有類型的構造器,方法或屬性的方式:getDeclaredXxxs(),返回數組。
  • 獲取指定的public類型的構造器,方法或屬性的方式:getXxx()
  • 獲取指定的構造器,方法或屬性的方式:getDeclaredXxx()

獲取的方式都比較相似。

Constructor

創建對象

newInstance(Object … initargs)

構造方法,使用該方法來創建對象,其中initargs爲初始化的參數值。Java中的另外一種創建對象的方式是使用new關鍵字。

如果獲取到的構造方法爲public類型,則可以直接調用newInstance方法創建。如果爲private,protected和缺省,則需要先調用setAccessible(true),設置其爲可訪問,然後在調用newInstance方法。

獲取註解

getAnnotation(Class annotationClass)

用於獲取構造器上的註解信息,annotationClass爲註解類的class。返回的爲註解類的對象,通過該對象可以獲取註解上的配置信息。

getParameterAnnotations()

用於獲取構造方法中的註解信息。返回值爲Annotation[][]。

Method

調用方法

invoke(Object obj, Object… args)

obj爲創建或獲取到的對象,args爲調用該方法時需要傳入的參數值。如果該Method對象爲非public的則需要先調用setAccessible(true),設置爲可訪問,才能使用invoke方法。如果不清楚method訪問限定類型,可以先使用getModifiers()獲取方法修飾符類型,再使用Modifier.isPublic()判斷是否爲public類型。如果Method爲靜態方法,則obj可以爲null。

獲取註解

getAnnotation(Class annotationClass)

獲取方法上配置的註解,annotationClass爲註解類的class。返回值爲註解對象。

Field

設置或獲取屬性

set(Object obj, Object value)
設置Field屬性值。obj爲創建或獲取到的對象,value爲設置到該屬性的值。如果該Field對象爲非public的則需要先調用setAccessible(true),設置爲可訪問,才能使用invoke方法。如果不清楚method訪問限定類型,可以先使用getModifiers()獲取方法修飾符類型,再使用Modifier.isPublic()判斷是否爲public類型。如果Field爲靜態方法,則obj可以爲null。

get(Object obj)
獲取Field屬性值。使用方式同上。

獲取註解

getAnnotation(Class annotationClass)

獲取屬性上配置的註解,annotationClass爲註解類的class。返回值爲註解對象。

小結

反射使用步驟:

1,通過Class.forName()獲取類的字節對象。
2,通過字節對象獲取到構造器Constructor

代理

代理分爲兩種:靜態代理,動態代理。
靜態代理是直接寫在代碼裏,通過一個類去操作需要代理的類。
動態代理則是通過反射和Proxy及相關的類在代碼運行時動態實現。

動態代理大致可以分爲3個步驟:
1,建立委託,即創建Interface(接口)和完成實現類;
2,實現InvocationHandler,在invoke方法內執行代理操作;
3,通過Proxy.newProxyInstance()方法創建代理對象,將代理對象傳遞給調用者。

Proxy

創建代理對象

newProxyInstance(ClassLoader loader, Class

InvocationHandler

這是一個接口,生成代理對象需要傳入該接口實現類的對象。

執行代理

invoke(Object proxy, Method method, Object[] args)
代理對象會調用這個方法來執行被代理的方法,也就是說我們可以在此根據需求對參數args進行修改或添加,或者執行其它的方法。

參數說明:
proxy:爲代理的對象,即通過Proxy.newProxyInstance()生成的。
method:代理對象需要執行的方法。
args:被調用的方法需要傳入的參數值。

測試

接口和實現類:

package com.reflect.test;

public interface Interface {

    void method0(String s);

    void method1(int i);
}

//修飾符缺省,只能包內訪問
class InterfaceImpl implements Interface{

    public InterfaceImpl(){}

    @Override
    public void method0(String s) {
        System.out.println(" s = " + s);
    }

    @Override
    public void method1(int i) {
        System.out.println(" i = " + i);
    }

}

InvocationHandler實現類和測試代碼

package com.reflect;

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

import com.reflect.test.Interface;

public class ProxyTest {

    public static void main(String[] args) {
        try {
            //獲取InterfaceImpl字節碼對象
            Class<?> clazz = Class.forName("com.reflect.test.InterfaceImpl");
            Constructor<?> cons = clazz.getConstructor();
            //設置爲可訪問
            cons.setAccessible(true);
            //創建對象
            Object obj = cons.newInstance();
            //創建代理對象
            Interface interf = (Interface) Proxy.newProxyInstance(
                    clazz.getClassLoader(), 
                    new Class<?>[]{Interface.class}, 
                    new InterfaceIH(obj));

            //代理對象調用方法
            interf.method0("call method0");
            interf.method1(1);

            clazz = Proxy.getProxyClass(clazz.getClassLoader(), new Class<?>[]{Interface.class});
            System.out.println(clazz.getCanonicalName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

//InvocationHandler實現類
class InterfaceIH implements InvocationHandler{
    //目標對象
    Object target;

    public InterfaceIH(Object obj){
        target = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        String name = method.getName();
        //被調用的方法名稱
        System.out.println("call method: " + name);
        //傳入的參數信息
        if(args != null && args.length > 0){
            System.out.println("arg = " + args[0]);
        }

        //修改參數
        if("method0".equals(name)){
            args[0] = "proxy " + args[0];
        }else if("method1".equals(name)){
            args[0] = 10 + (Integer)args[0];
        }
        return method.invoke(target, args);
    }

}

測試結果:

call method: method0
arg = call method0
 s = proxy call method0
call method: method1
arg = 1
 i = 11
com.sun.proxy.$Proxy0

參考博客:

公共技術點之 Java 動態代理
http://a.codekk.com/detail/Android/Caij/%E5%85%AC%E5%85%B1%E6%8A%80%E6%9C%AF%E7%82%B9%E4%B9%8B%20Java%20%E5%8A%A8%E6%80%81%E4%BB%A3%E7%90%86

深度剖析JDK動態代理機制
http://www.cnblogs.com/MOBIN/p/5597215.html

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