Java反射與代理


Java反射機制與動態代理,使得Java更加強大,Spring核心概念IoCAOP就是通過反射機制與動態代理實現的。

1       Java反射

示例:

User user = new User();
user.setTime5Flag("test");
 
Class<?> cls = Class.forName("com.test.User");
//接口必須public,無論是否在本類內部使用!或者使用cls.getDeclaredMethod(),或者遍歷修改可訪問性
Method method = cls.getMethod("getTime5Flag");
String res1 = (String) method.invoke(user);
System.out.println(res1);
//涉及到基本類型如int,則使用int.class!Integer.class!=int.class!
method = cls.getMethod("setTime5Flag", String.class); 
method.invoke(user, "Rollen");
method = cls.getMethod("getTime5Flag");
String res2 = (String) method.invoke(user);
System.out.println(res2);

 

通過一個對象獲得完整的包名和類名:

user.getClass().getName();//全路徑類名
user.getClass().getSimpleName();//無包名的類名

 

獲取class:
Class.forName("com.test.User");
com.test.User.class;
user.getClass();

 

通過class實例化一個對象
User user = (User) cls.newInstance();//必須有無參構造函數

 

取得全部構造函數
Constructor<?> cons[]=cls.getConstructors(); //按聲明順序返回
cons[0].newInstance();//無顯示聲明,則有默認構造函數

 

取得一個類所實現的所有interface 
Class<?> intes[] = cls.getInterfaces();

 

取得父類
cls.getSuperClass();

 

取得修飾符
int mo = cls.getModifiers();
int mo = cons[0].getModifiers();
int mo = method.getModifiers();
Modifier.toString(mo);

 

獲取方法參數
method.getParametors();
cons[0].getParametors();

 

獲取方法參數類型
method.getParametorTypes();
cons[0].getParametorTypes();

 

獲取方法聲明拋出的所有異常類型
method.getExceptionTypes();

 

獲取本類聲明的全部屬性
Field[] field = cls.getDeclaredFields(); //包括private
field[0].getModifiers();
field[0].getType();

 

獲取本類的全部公開屬性,包括父類聲明、接口聲明、本類聲明的所有public屬性

cls.getFields();

 

設置指定屬性可訪問

field.setAccessible(true);
field.set(obj,’ces’);
field.get(obj);

 

* getFields()與getDeclaredFields()區別:getFields()只能訪問類中聲明為公有的字段,私有的字段它無法訪問,能訪問從其它類繼承來的公有字段;getDeclaredFields()能訪問類中所有的字段,與public,private,protect無關,但不能訪問從其它類繼承來的字段

* getMethods()與getDeclaredMethods()區別:getMethods()只能訪問類中聲明為公有的方法,私有的方法它無法訪問,能訪問從其它類繼承來的公有方法;getDeclaredMethods()能訪問類中所有的字段,與public,private,protect無關,不能訪問從其它類繼承來的方法

* getConstructors()與getDeclaredConstructors()區別:getConstructors()只能訪問類中聲明為public的構造函數;getDeclaredConstructors()能訪問類中所有的構造函數,與public,private,protect無關

 

通過反射獲取並修改數組的信息
int[] temp={1,2,3,4,5};
Class<?> demo = temp.getClass().getComponentType();
System.out.println("數組類型: "+demo.getName());//int
System.out.println("數組長度: "+Array.getLength(temp));//5
System.out.println("數組的第一個元素: "+Array.get(temp, 0));//1
Array.set(temp, 0, 100);
System.out.println("修改之后數組第一個元素為: "+Array.get(temp, 0));//100

 

獲取數組類型
cls.getComponentType();

 

判斷是否是數組類型
cls.isArray();

 

2       Java代理

代理模式是常用的Java設計模式,它的特征是代理類與委托類有同樣的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事后處理消息等。代理類與委托類之間通常會存在關聯關系,一個代理類的對象與一個委托類的對象關聯,代理類的對象本身並不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。

按照代理的創建時期,代理類可以分為2種。

  • 靜態代理:由程序員創建或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。
  • 動態代理:在程序運行時,由Java反射機制動態生成字節碼。

2.1   靜態代理

public interface Count { 
    public void queryCount();
}
public class CountImpl implements Count {
    public void queryCount() { 
        System.out.println("查看賬戶方法...");
    }
}
//代理類
public class CountProxy implements Count { 
    private CountImpl countImpl;   
    public CountProxy(CountImpl countImpl) { 
        this.countImpl = countImpl; 
    }   
    @Override 
    public void queryCount() { 
        System.out.println("事務處理之前");           
        countImpl.queryCount(); // 調用委托類的方法;
        System.out.println("事務處理之后"); 
    }
} 
//測試類
public class TestCount { 
    public static void main(String[] args) { 
        CountImpl countImpl = new CountImpl(); 
        CountProxy countProxy = new CountProxy(countImpl); 
        countProxy.queryCount();   
    } 
}

 

觀察代碼可以發現每一個代理類只能為一個接口服務,這樣一來程序開發中必然會產生過多的代理,而且,所有的代理操作除了調用的方法不一樣之外,其他的操作都一樣,則此時肯定是重復代碼。解決這一問題最好的做法是可以通過一個代理類完成全部的代理功能,那么此時就必須使用動態代理完成。

 

2.2   動態代理

動態代理類的字節碼在程序運行時由Java反射機制動態生成,無需程序員手工編寫它的源代碼。動態代理類不僅簡化了編程工作,而且提高了軟件系統的可擴展性,因為Java 反射機制可以生成任意類型的動態代理類。

2.2.1  JDK動態代理

java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動態代理類的能力。

InvocationHandler接口: 

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

參數說明: 

Object proxy:指被代理的對象。 
Method method:要調用的方法 
Object[] args:方法調用時所需要的參數 

可以將InvocationHandler接口的子類想象成一個代理的最終操作類,替換掉ProxySubject。 

Proxy類: 

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

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

參數說明: 

ClassLoader loader:類加載器 
Class<?>[] interfaces:得到全部的接口 
InvocationHandler h:得到InvocationHandler接口的子類實例 

 

如果想要完成動態代理,首先需要定義一個InvocationHandler接口的子類,以完成代理的具體操作。

interface Subject {
    public String say(String name, int age);
}
class RealSubject implements Subject {
    @Override
    public String say(String name, int age) {
        return name + "  " + age;
    }
}
//JDK動態代理類
class MyInvocationHandler implements InvocationHandler {
    private Object target = null;
    //綁定委托對象並返回一個代理類
    public Object bind(Object target) {
        this. target = target;
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
          target.getClass().getInterfaces(),
this); //要綁定接口(cglib彌補了這一點) } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(“before method!”); Object temp = method.invoke(target, args); System.out.println(“after method!”); return temp; } } class hello { public static void main(String[] args) { MyInvocationHandler demo = new MyInvocationHandler(); Subject sub = (Subject) demo.bind(new RealSubject()); String info = sub.say("Rollen", 20); System.out.println(info); } }

 

但是,JDK的動態代理依靠接口實現,如果有些類並沒有實現接口,則不能使用JDK代理,這就要使用cglib動態代理了。 
 

2.2.2  CGLIB動態代理

JDK的動態代理機制只能代理實現了接口的類,而未實現接口的類就不能實現JDK的動態代理。

cglib是針對類來實現代理的,它的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現增強,但因為采用的是繼承,所以不能對final修飾的類進行代理。 

public interface BookFacade { 
    public void addBook(); 
} 
public class BookFacadeImpl1 { 
    public void addBook() { 
        System.out.println("增加圖書的普通方法..."); 
    } 
} 
 
import java.lang.reflect.Method;   
import net.sf.cglib.proxy.Enhancer; 
import net.sf.cglib.proxy.MethodInterceptor; 
import net.sf.cglib.proxy.MethodProxy; 
//cglib動態代理類
public class BookFacadeCglib implements MethodInterceptor { 
    private Object target;    
    //綁定委托對象並返回一個代理類
    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 { 
        System.out.println("事物開始"); 
        Object temp = proxy.invokeSuper(obj, args); 
        System.out.println("事物結束"); 
        return temp;   
    } 
} 
public class TestCglib {
    public static void main(String[] args) { 
        BookFacadeCglib cglib = new BookFacadeCglib(); 
        BookFacadeImpl1 bookCglib = (BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1()); 
        bookCglib.addBook(); 
    } 
}

 

參考:
1.https://segmentfault.com/a/1190000004326040
2.http://blog.csdn.net/hintcnuie/article/details/10954631


免責聲明!

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



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