一篇博客读懂设计模式之---动态代理与反射
一篇博客讀懂設計模式之---動態代理與反射
?
先來講一下反射:
1 關于反射
反射最大的作用之一就在于我們可以不用在編譯時就知道某個對象的類型,而在運行時通過提供完整的”包名+類名.class”得到。注意:不是在編譯時,而是在運行時。
功能:
?在運行時能判斷任意一個對象所屬的類。
?在運行時能構造任意一個類的對象。
?在運行時判斷任意一個類所具有的成員變量和方法。
?在運行時調用任意一個對象的方法。
說大白話就是,利用Java反射機制我們可以加載一個運行時才得知名稱的class,獲悉其構造方法,并生成其對象實體,能對其fields設值并喚起其methods。
應用場景:
反射技術常用在各類通用框架開發中。因為為了保證框架的通用性,需要根據配置文件加載不同的對象或類,并調用不同的方法,這個時候就會用到反射——運行時動態加載需要加載的對象。
缺點:
由于反射會額外消耗一定的系統資源,因此如果不需要動態地創建一個對象,那么就不需要用反射。另外,反射調用方法時可以忽略權限檢查,因此可能會破壞封裝性而導致安全問題。
2 動態代理
為其他對象提供一種代理以控制對這個對象的訪問。在某些情況下,一個對象不適合或者不能直接引用另一個對象,而代理對象可以在兩者之間起到中介的作用(可類比房屋中介,房東委托中介銷售房屋、簽訂合同等)。
所謂動態代理,就是實現階段不用關心代理誰,而是在運行階段才指定代理哪個一個對象(不確定性)。如果是自己寫代理類的方式就是靜態代理(確定性)。
組成要素:
(動態)代理模式主要涉及三個要素:
其一:抽象類接口
其二:被代理類(具體實現抽象接口的類)
其三:動態代理類:實際調用被代理類的方法和屬性的類
?
動態代理有三個必要條件:
1. 要有兩個角色(代理對象和被代理對象)
2. 代理對象要持有被代理對象的引用(要有被代理對象的信息)
3. 注重過程,必須要做的事情,被代理對象沒時間或者不想做
?
實現方式:
實現動態代理的方式很多,比如 JDK 自身提供的動態代理,就是主要利用了反射機制。還有其他的實現方式,比如利用字節碼操作機制,類似 ASM、CGLIB(基于 ASM)、Javassist 等。
?
舉例,??刹捎玫?/span>JDK提供的動態代理接口InvocationHandler來實現動態代理類。其中invoke方法是該接口定義必須實現的,它完成對真實方法的調用。通過InvocationHandler接口,所有方法都由該Handler來進行處理,即所有被代理的方法都由InvocationHandler接管實際的處理任務。此外,我們??梢栽趇nvoke方法實現中增加自定義的邏輯實現,實現對被代理類的業務邏輯無侵入。
?
代理模式在我們生活中其實都有很多形象的例子:比如媒婆,租房中介等等,都是典型的動態代理模式,下面我們就以大家比較熟悉的媒婆來舉例,更加生動形象的來介紹動態代理:
首先先用JDK來實現動態代理,必要條件:被代理對象要實現一個接口:
?
public interface Person {void findGF(); } public class James implements Person{@Overridepublic void findGF() {System.out.println("高富帥");System.out.println("有房有車的");System.out.println("身高要求180cm以上,體重70kg");} } public class Meipo implements InvocationHandler {private Object target; //被代理對象的引用作為一個成員變量保存下來了//獲取被代理人的個人資料public Object getInstance(Object target) throws Exception{this.target = target;Class clazz = target.getClass();System.out.println("被代理對象的class是:"+clazz);return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("我是媒婆:" + "得給你找個異性才行");System.out.println("開始進行海選...");System.out.println("------------");//調用被代理類的方法,這里就會調用James的方法method.invoke(this.target, args);System.out.println("------------");System.out.println("如果合適的話,就準備辦事");return null;} }?
測試類:
public class TestJDKProxy {public static void main(String[] args) {try{Person person = (Person) new JDKMeiPo().getInstance(new XiaoJie());//下面調用findGF()就會調用invoke()方法person.findGF();}catch (Exception e){e.printStackTrace();}} }?
?
下面用CGLIB方式來實現動態代理:CGLIB相比JDK的方式優勢是:被代理類不一定要去實現接口,它是通過CGLIB來生成被代理類的子類,繼承父類的方法和屬性,從而達到代理的效果:
public class James {public void findLove(){System.out.println("膚白貌美大長腿");} }代理類需要實現MethodInterceptor接口
public class Meipo implements MethodInterceptor{//好像并沒有持有被代理對象的引用?public Object getInstance(Class clazz) throws Exception{Enhancer enhancer = new Enhancer();//把父類設置為誰?//這一步就是告訴cglib,生成的子類需要繼承哪個類enhancer.setSuperclass(clazz);//設置回調enhancer.setCallback(this);//第一步、生成源代碼//第二步、編譯成class文件//第三步、加載到JVM中,并返回被代理對象return enhancer.create();}//同樣是做了字節碼重組這樣一件事情//對于使用API的用戶來說,是無感知@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {System.out.println("我是媒婆:" + "得給你找個異性才行");System.out.println("開始進行海選...");System.out.println("------------");//這個obj的引用是由CGLib給我們new出來的//cglib new出來以后的對象,是被代理對象的子類(繼承了我們自己寫的那個類)//OOP, 在new子類之前,實際上默認先調用了我們super()方法的,//new了子類的同時,必須先new出來父類,這就相當于是間接的持有了我們父類的引用//子類重寫了父類的所有的方法//我們改變子類對象的某些屬性,是可以間接的操作父類的屬性的proxy.invokeSuper(obj, args);System.out.println("------------");System.out.println("如果合適的話,就準備辦事");return null;} }?
public class TestGglibProxy {public static void main(String[] args) {//JDK的動態代理是通過接口來進行強制轉換的//生成以后的代理對象,可以強制轉換為接口//CGLib的動態代理是通過生成一個被代理對象的子類,然后重寫父類的方法//生成以后的對象,可以強制轉換為被代理對象(也就是用自己寫的類)//子類引用賦值給父類try {James obj = (James)new Meipo().getInstance(James.class);obj.findLove();} catch (Exception e) {e.printStackTrace();}} }?
額外:
??靜態代理:事先寫好代理類,可以手工編寫,也可以用工具生成。缺點是每個業務類都要對應一個代理類,非常不靈活。
? ?動態代理:運行時自動生成代理對象。缺點是生成代理代理對象和調用代理方法都要額外花費時間。
? JDK動態代理:基于Java反射機制實現,必須要實現了接口的業務類才能用這種辦法生成代理對象。新版本也開始結合ASM機制。
? ?cglib動態代理:基于ASM機制實現,通過生成業務類的子類作為代理類。
Java 發射機制的常見應用:動態代理(AOP、RPC)、提供第三方開發者擴展能力(Servlet容器,JDBC連接)、第三方組件創建對象(DI)……
?
?
?
總結
以上是生活随笔為你收集整理的一篇博客读懂设计模式之---动态代理与反射的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL 基础 ———— 子查询
- 下一篇: Jackson快速入门