java反射和動態代理

反射

反射的官方定義是這樣的:java

    在運行狀態中,對於任意的一個類,都可以知道這個類的全部屬性和方法,對任意一個對象都可以經過反射機制調用一個類的任意方法,這種動態獲取類信息及動態調用類對象方法的功能稱爲java的反射機制。api

通常建立類的方式:ide

        Car car = new Car();
        car.setName("寶馬");

而反射則是一開始並不知道我要初始化的類對象是什麼,天然也沒法使用 new 關鍵字來建立對象了。這時候,咱們使用 JDK 提供的反射 API 進行反射調用:函數

        Class clz = Class.forName("Car");
        Method method = clz.getMethod("setName", String.class);
        Constructor constructor = clz.getConstructor();
        Object object = constructor.newInstance();
        method.invoke(object, "寶馬");

 

jdk提供了三種方式獲取一個對象的Class,就Car car來講測試

1.car.getClass(),這個是Object類裏面的方法ui

2.Car.Class屬性,任何的數據類型,基本數據類型或者抽象數據類型,均可以經過這種方式獲取類this

3.Class.forName(""),Class類提供了這樣一個方法,讓咱們經過類名來獲取到對象類spa

這三種方法用的最多的就是第三種,那麼獲取到類以後,Class類提供了不少獲取類屬性,方法,構造方法的api。代理

 

經過反射建立類對象主要有兩種方式:經過 Class 對象的 newInstance() 方法、經過 Constructor 對象的 newInstance() 方法。code

第一種:經過 Class 對象的 newInstance() 方法。

        Class clz = Car.class;
        Car car = (Car)clz.newInstance();

第二種:經過 Constructor 對象的 newInstance() 方法

        Class clz = Car.class;
        Constructor constructor = clz.getConstructor();
        Car car = (Car)constructor.newInstance();

經過 Constructor 對象建立類對象能夠選擇特定構造方法,而經過 Class 對象則只能使用默認的無參數構造方法。下面的代碼就調用了一個有參數的構造方法進行了類對象的初始化。

        Class clz = Car.class;
        Constructor constructor = clz.getConstructor(String.class);
        Car car = (Car) constructor.newInstance("寶馬");

 

經過反射獲取類屬性、方法、構造器:

getDeclaredMethods() 獲取全部的方法

getReturnType() 得到方法的放回類型

getParameterTypes() 得到方法的傳入參數類型

getDeclaredMethod("方法名",參數類型.class,……) 得到特定的方法

getDeclaredConstructors() 獲取全部的構造方法

getDeclaredConstructor(參數類型.class,……) 獲取特定的構造方法

getSuperclass() 獲取某類的父類

getInterfaces() 獲取某類實現的接口

getFields():得到某個類的全部的公共(public)的字段,包括父類中的字段。 
getDeclaredFields():得到某個類的全部聲明的字段,即包括public、private和proteced,可是不包括父類的申明字段。

一樣相似的還有getConstructors()和getDeclaredConstructors()、getMethods()和getDeclaredMethods(),這二者分別表示獲取某個類的方法、構造函數。

通常狀況下,咱們並不能對類的私有字段進行操做,利用反射也不例外,但有的時候,例如要序列化的時候,咱們又必須有能力去處理這些字段,這時候,咱們就須要調用AccessibleObject上的setAccessible()方法來容許這種訪問,而因爲反射類中的Field,Method和Constructor繼承自AccessibleObject,所以,經過在這些類上調用setAccessible()方法,咱們能夠實現對這些字段的操做。

例子:

Car.java
public class Car {
    private String name;

    public Car(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
若是在Car.java內部建立mian方法測試的時候,是不會報異常的。
TestReflect.java
import java.lang.reflect.Field;

public class TestReflect {
    public static void main(String[] args) throws Exception {
        Car car = new Car("寶馬");
        Class clz = Car.class;
        Field name = clz.getDeclaredField("name");
//        name.setAccessible(true);
        System.out.println(name.get(car));//獲取當前對象中當前Field的value
    }
}

測試報異常:

Exception in thread "main" java.lang.IllegalAccessException: Class TestReflect can not access a member of class Car with modifiers "private"
    at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
    at java.lang.reflect.AccessibleObject.slowCheckMemberAccess(AccessibleObject.java:296)
    at java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:288)
    at java.lang.reflect.Field.get(Field.java:390)
    at TestReflect.main(TestReflect.java:9)

取消註釋後正常

 動態代理

代理模式的定義:爲其餘對象提供一種代理以控制對這個對象的訪問。在某些狀況下,一個對象不適合或者不能直接引用另外一個對象,而代理對象能夠在客戶端和目標對象之間起到中介的做用。而代理模式又分爲靜態代理和動態代理。

靜態代理通俗點將就是本身手寫一個代理類,而動態代理則不用咱們手寫,而是依賴於java反射機制。

Subject.java

public interface Subject {
    void doSomething();
}

RealSubject.java

public class RealSubject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("doSomething");
    }
}

ProxyObject.java

public class ProxyObject implements Subject {
    @Override
    public void doSomething() {
        System.out.println("proxy Subject");
        new RealSubject().doSomething();
    }

    public static void main(String[] args) {
        new ProxyObject().doSomething();
    }
}

ProxyObject就是RealSubject的靜態代理類,代替了RealSubject完成doSomething的工做。

 

Java中動態代理的實現,關鍵就是這兩個東西:Proxy、InvocationHandler

ProxyHandler.java

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

public class ProxyHandler implements InvocationHandler {
    private Object realObject = null;

    public ProxyHandler(Object proxied) {
        this.realObject = proxied;
    }

    public Subject getProxy() {
        return (Subject) Proxy.newProxyInstance(realObject.getClass().getClassLoader(),
                realObject.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before invoke");
        return method.invoke(realObject, args);
    }

    public static void main(String[] args) {
        ProxyHandler proxyHandler = new ProxyHandler(new RealSubject());
        Subject subject = proxyHandler.getProxy();
        subject.doSomething();
    }
}

動態代理,必須先實現這個InvocationHandler接口,而後經過Proxy生成一個代理對象。

方法裏面有一個Proxy類,這個Proxy類提供了不少方法,這裏咱們用的是newProxyInstance方法,它有三個參數,第一個是被代理類的類構造器,第二個指的是被代理類的接口,也就是Subject接口,第三個是實現這個代理的類,這裏就是本類。具體的來講,這個方法執行了下面三步:

1.生成一個實現了參數interfaces裏全部接口且繼承了Proxy的代理類的字節碼,而後用參數裏的classLoader加載這個代理類。

2.使用代理類父類的構造函數 Proxy(InvocationHandler h)來創造一個代理類的實例,將咱們自定義的InvocationHandler的子類傳入。

3.返回這個代理類實例,由於咱們構造的代理類實現了interfaces(也就是咱們程序中傳入的realObject.getClass().getInterfaces())裏的全部接口,所以返回的代理類能夠強轉成Subject類型來調用接口中定義的方法。

 

能夠添加打印生成的代理對象的相關信息已方便分析:

新的ProxyHandler.java

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

public class ProxyHandler implements InvocationHandler {
    private Object realObject = null;

    public ProxyHandler(Object proxied) {
        this.realObject = proxied;
    }

    public Subject getProxy() {
        return (Subject) Proxy.newProxyInstance(realObject.getClass().getClassLoader(),
                realObject.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before invoke");
        return method.invoke(realObject, args);
    }

    public static void main(String[] args) {
        ProxyHandler proxyHandler = new ProxyHandler(new RealSubject());
        Subject subject = proxyHandler.getProxy();

        //這裏能夠經過運行結果證實subject是Proxy的一個實例,這個實例實現了Subject接口
        System.out.println(subject instanceof Proxy);

        //這裏能夠看出subject的Class類是$Proxy0,這個$Proxy0類繼承了Proxy,實現了Subject接口
        System.out.println("subject的Class類是:" + subject.getClass().toString());
        System.out.print("subject中的屬性有:");
        Field[] field = subject.getClass().getDeclaredFields();
        for (Field f : field) {
            System.out.print(f.getName() + ", ");
        }
        System.out.print("\n" + "subject中的方法有:");
        Method[] method = subject.getClass().getDeclaredMethods();
        for (Method m : method) {
            System.out.print(m.getName() + ", ");
        }

        System.out.print("\n" + "subject的父類是:" + subject.getClass().getSuperclass());
        System.out.print("\n" + "subject實現的接口是:");
        Class<?>[] interfaces = subject.getClass().getInterfaces();
        for (Class<?> i : interfaces) {
            System.out.print(i.getName() + ", ");
        }

        System.out.println("\n\n" + "運行結果爲:");
        subject.doSomething();
    }
}

運行獲得結果:

true
subject的Class類是:class com.sun.proxy.$Proxy0
subject中的屬性有:m1, m3, m2, m0, 
subject中的方法有:equals, toString, hashCode, doSomething, 
subject的父類是:class java.lang.reflect.Proxy
subject實現的接口是:Subject, 

運行結果爲:
before invoke
doSomething

分析Proxy類源代碼

newProxyInstance方法功能:

(1)根據參數loader和interfaces調用方法 getProxyClass(loader, interfaces)建立代理類$Proxy0.$Proxy0類 實現了interfaces的接口,並繼承了Proxy類. 
(2)實例化$Proxy0並在構造方法中把DynamicSubject傳過去,接着$Proxy0調用父類Proxy的構造器,爲h賦值,以下

    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

爲了分析$Proxy0,能夠生成 Proxy0的class文件,而後反編譯出代碼。

生成class文件的函數:

    public static void getProxy0Code() {
        byte[] classFile = ProxyGenerator.generateProxyClass("Proxy0", Subject.class.getInterfaces());
        try {
            File file = new File("Proxy0.class");
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(classFile);
            fos.flush();
            fos.close();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
        }
    }

經過jd-gui反編譯出源代碼

來看一下這個繼承了Proxy的$Proxy0的源代碼: 

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 Subject {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;

    public Proxy0(InvocationHandler paramInvocationHandler) {
        super(paramInvocationHandler);
    }

    public final boolean equals(Object paramObject) {
        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 doSomething() {
        try {
            this.h.invoke(this, m3, null);
            return;
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

    public final String toString() {
        try {
            return (String) this.h.invoke(this, m2, null);
        } catch (Error | RuntimeException localError) {
            throw localError;
        } catch (Throwable localThrowable) {
            throw new UndeclaredThrowableException(localThrowable);
        }
    }

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

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
            m3 = Class.forName("Subject").getMethod("doSomething", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            return;
        } catch (NoSuchMethodException localNoSuchMethodException) {
            throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        } catch (ClassNotFoundException localClassNotFoundException) {
            throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
    }
}

接着把獲得的$Proxy0實例強制轉換成Subject,並將引用賦給subject。當執行subject.doSomething()方法時,就調用 了$Proxy0類中的doSomething()方法,進而調用父類Proxy中的h的invoke()方法.即 InvocationHandler.invoke()。 

 newProxyInstance生成代理對象的詳細分析過程參考

相關文章
相關標籤/搜索