Java反射與動態代理


  Java反射機制可以動態地獲取類的結構,動態地調用對象的方法,是java語言一個動態化的機制。java動態代理可以在不改變被調用對象源碼的前提下,在被調用方法前后增加自己的操作,極大地降低了模塊之間的耦合性。這些都是java的基礎知識,要想成為一名合格的程序猿,必須掌握!

Java反射機制

   JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。 

   Java反射機制允許程序在運行時判斷分析任意一個類的結構,包括成員變量和方法,並調用任意一個對象的方法。Eclipse可以自動彈出對象的方法及屬性,就是利用了反射的原理Java的動態代理就是利用了反射的特性來實現的

1、獲取類Class對象

   1) A.class :不會加載類,也不會執行靜態代碼段;

   2) Class.forName("cn.test.reflect.A") :要求JVM查找並加載指定的類,也就是說JVM會執行該類的靜態代碼段;

   3) new A().getClass() :通過對象獲取class

2、反射創建對象

   1) 通過默認構造函數創建對象:Class<?> t = Class.forName("cn.test.reflect.A"); t.newInstance();

   2) 通過指定構造函數創建對象:Class<?> t = Class.forName("cn.test.reflect.A"); Constructor<?> cons[] = t.getConstructors(); A a = (A) cons[2].newInstance("aa","bb");

   注:

   ① Class<?>表示任何類型的類

   ② newInstance()方法只能調用public的無參構造函數,它和new關鍵字創建實例的區別:創建對象的方式不一樣,前者是使用類加載機制,后者是創建一個新類

3、Class類常用方法

   ▶ getName() :獲得類的完整名字;

   ▶ getSuperclass() :獲得類的父類;

   ▶ newInstance() :通過類的不帶參數的構造方法創建這個類的一個對象;

   ▶ getFields() :獲得當前類和父類中的public類型的所有屬性;

   ▶ getDeclaredFields() :獲得當前類(不包含父類)聲明的所有屬性,包括private和public

    注:對於某個屬性field,設置field.setAccessible(true),即可訪問private的屬性值,如field.get(obj)

   ▶ getMethods() :獲得前類和父類中public類型的所有方法

   ▶ getDeclaredMethods() :獲得當前類(不包含父類)聲明的所有方法,包括private和public

   ▶ getMethod(String name, Class[] parameterTypes) 獲得類的指定方法,name參數指定方法的名字,parameterTypes 參數指定方法的參數類型;

   ▶ getConstructors() :獲得當前類的public類型的構造方法

   ▶ getDeclaredConstructors() :獲得當前類的public和private類型的構造方法

   ▶ getConstructor(Class[] parameterTypes) :獲得類的特定構造方法,parameterTypes 參數指定構造方法的參數類型;

   ▶ getInterfaces() :獲得實現的接口;

   ▶ getSuperclass() :獲得繼承的父類;

4、Java反射使用實例

package com.hicoor.test.reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;

class B{
    public int b;
    public B(){}
}

interface IA{}

class A extends B implements IA{
    public A() { }

    public A(String str) { }

    public A(String str1, String str2) { }

    private String str;
    public int age;

    public int func1(String name) {
        System.out.println("hello " + name);
        return 8;
    }

    public void func1(String name1, String name2) {
        System.out.println("hello "+name1+","+name2);
    }
}

public class ReflectDemo {
    
    public static void main(String[] args) throws Exception {
        // 根據字符串獲得類
        Class<?> demoClass = Class.forName("com.hicoor.test.reflect.A");

        // 獲得類的完整名字
        System.out.println("類名"+demoClass.getName()); // com.hicoor.test.reflect.A
        // 獲得類加載器,默認sun.misc.Launcher$AppClassLoader
        System.out.println("類加載器:"+demoClass.getClassLoader().getClass().getName());

        //根據Class的共有無參構造方法創建一個實例
        A newAObj = (A)demoClass.newInstance();
        
        // 獲得類中聲明的屬性
        Field[] publicFields = demoClass.getFields(); // 獲得當前類和父類中的public類型的所有屬性,返回:age
        Field[] declareFields = demoClass.getDeclaredFields(); // 獲得當前類(不包含父類)聲明的所有屬性,包括private和public,返回:str age
        Field specifyField = demoClass.getField("age"); // 根據名稱獲取指定屬性
        specifyField.setAccessible(true);
        //修改屬性
        specifyField.set(newAObj, 88);
        
        // 獲得類的方法
        Method[] publicMethods = demoClass.getMethods(); // 獲得前類和父類中public類型的所有方法
        Method[] declareMethods = demoClass.getDeclaredMethods(); // 獲得當前類(不包含父類)聲明的所有方法,包括private和public
        Method specifyMethod = demoClass.getDeclaredMethod("func1",new Class<?>[]{java.lang.String.class});  //根據方法名和方法參數類型指定獲取一個方法
        //反射調用對象的方法
        specifyMethod.invoke(newAObj, "hans");
        
        //獲得構造函數
        Constructor<?>[] publicConstructors = demoClass.getConstructors();
        Constructor<?>[] declareConstructors = demoClass.getDeclaredConstructors();  //獲得當前類聲明的所有private和public構造方法
        Constructor<?> constructor = demoClass.getConstructor(new Class<?>[]{java.lang.String.class});  //根據指定類型獲得構造方法
        A newAobj2 = (A)constructor.newInstance("hello");  //根據指定構造函數創建實例
        
        //獲得實現的接口
        Class<?>[] interfaces = demoClass.getInterfaces();
        
        //獲得繼承的父類
        Class<?> superclass = demoClass.getSuperclass();
    }

    //反射獲得一個方法的明細定義
    private static void getMethodDetail(Method method) {
        String methodModifier = Modifier.toString(method.getModifiers());  //方法修飾符
        String returnType = method.getReturnType().getName();  //方法返回值
        Class<?>[] parameterTypes = method.getParameterTypes();  //方法參數類型
        System.out.print(methodModifier+" "+returnType+" "+ method.getName()+"(");
        int i=1;
        for (Class<?> parameterType : parameterTypes) {
            System.out.print(parameterType.getName() + " arg"+(i++));
            if(i<=parameterTypes.length){
                System.out.print(",");
            }
        }
        System.out.println(") {}");
    }

}
View Code
JAVA動態代理

   假如有這樣的需求,要在某些模塊方法調用前后加上一些統一的前后處理操作,比如在添加購物車、修改訂單等操作前后統一加上登陸驗證與日志記錄處理,該怎樣實現?首先想到最簡單的就是直接修改源碼,在對應模塊的對應方法前后添加操作。如果模塊很多,你會發現,修改源碼不僅非常麻煩、難以維護,而且會使代碼顯得十分臃腫。

   這時候就輪到動態代理上場了,它可以通過反射在被調用方法前后加上自己的操作,而不需要更改被調用類的源碼,大大地降低了模塊之間的耦合性,體現了極大的優勢

1、JDK動態代理

   JDK動態代理中包含一個類和一個接口,即Proxy類和InvocationHandler接口。

   1) InvocationHandler接口:

public interface InvocationHandler { 
  public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 
} 

   參數說明:① Object proxy:指被代理的對象;② Method method:要調用的方法;③ Object[] args:方法調用時所需要的參數;

   2) Proxy類: Proxy類是專門完成代理的操作類,可以通過此類為一個或多個接口動態地生成實現類,此類提供了如下的操作方法。

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

   參數說明:① ClassLoader loader:類加載器;② Class<?>[] interfaces:得到全部的接口;③ InvocationHandler h:得到InvocationHandler接口的子類實例 ;

   3) 關於類加載器

   在Proxy類中的newProxyInstance()方法中需要一個ClassLoader類的實例,ClassLoader實際上對應的是類加載器,在Java中主要有以下三種類加載器:

   ① Booststrap ClassLoader:此加載器采用C++編寫,通常加載jre/lib/rt.jar,一般開發中是看不到的; 

   ② Extendsion ClassLoader:用來進行擴展類的加載,通常加載jre/lib/ext/*.jar; 

   ③ AppClassLoader:(默認)加載classpath指定的類,是最常使用的是一種加載器;

   4) JDK動態代理實例:

package com.hicoor.test.dynamicProxy;

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

interface Animal {
    public void makeSound(String name);
}

class Dog implements Animal {
    @Override
    public void makeSound(String name) {
        System.out.println("Hi," + name + ",wang,wang~~");
    }
}

class Cat implements Animal {
    @Override
    public void makeSound(String name) {
        System.out.println("Hi," + name + ",miao,miao~~");
    }
}

/**
 * @author Hans 通用動態代理類,被調用對象方法前后增加特殊操作一樣的類可都用此類代理
 */
class AnimalProxy implements InvocationHandler {
    // 要代理的對象
    private Object target;

    /**
     * 綁定委托對象並返回一個代理類
     * 
     * @param target
     * @return
     */
    public Object getInstance(Object target) {
        this.target = target;
        // 取得代理對象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
        Object result = null;
        System.out.println("方法調用前操作..");
        // 執行被調用方法主體
        result = method.invoke(target, args);
        System.out.println("方法調用后操作..");
        return result;
    }

}

public class DynamicProxyJDKDemo {
    public static void main(String[] args) {
        AnimalProxy proxy = new AnimalProxy();
        Animal dogProxy = (Animal) proxy.getInstance(new Dog());
        dogProxy.makeSound("Tom");
    }
}
View Code

2、Cglib動態代理

   JDK的動態代理機制只能代理實現了接口的類,而不能實現接口的類就不能實現JDK的動態代理,cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現增強,但因為采用的是繼承,所以不能對final修飾的類進行代理

   使用cglib代理需下載cglib.jar文件,下載地址:http://www.java2s.com/Code/Jar/c/Downloadcloudcglibjar.htm

   Cglib動態代理實例:

package com.hicoor.test.dynamicProxy;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;


class Snake{
    public void makeSound(String name) {
        System.out.println("Hi," + name + ",si,si~~");
    }
}

class AnimalProxyCglib implements MethodInterceptor {
    // 要代理的對象
    private Object target;

    /**
     * 創建代理對象
     * 
     * @param target
     * @return
     */
    public Object getInstance(Object target) {
        this.target = target;
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.target.getClass());
        // 回調方法
        enhancer.setCallback(this);
        // 創建代理對象
        return enhancer.create();
    }

    @Override
    public Object intercept(Object obj, Method method, Object[] args,
            MethodProxy proxy) throws Throwable {
        Object result = null;
        System.out.println("方法調用前操作..");
        // 執行被調用方法主體
        result = proxy.invokeSuper(obj, args);
        System.out.println("方法調用后操作..");
        return result;
    }

}

public class DynamicProxyCglibDemo {

    public static void main(String[] args) {
        AnimalProxyCglib proxy = new AnimalProxyCglib();
        Snake dogProxy = (Snake) proxy.getInstance(new Snake());
        dogProxy.makeSound("Tom");
    }
    
}
View Code


免責聲明!

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



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