目錄
0、前言:
1、裝飾設計模式:
2、代理模式之靜态代理
3、代理模式之動态代理
3.1、JDK動态代理:
3.2CGLIB動态代理
在Spring中:
0、前言:
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5iMwITNyYGMkVDOzYTZ4IGOxYzX3IDOxcTMwEzLcFTMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL5M3Lc9CX6MHc0RHaiojIsJye.png)
對于上一篇文章:
上述例子存在這麼一個問題:
在我們的業務層中每一個業務方法都得處理事務(繁瑣的try-catch).
在設計上存在兩個很嚴重問題:
1):責任不分離.業務方法隻需要關心如何完成該業務功能,不需要去關系事務管理/日志管理/權限管理等等.
2):代碼結構重複.在開發中不要重複代碼,重複就意味着維護成本增大.
引出課堂上的這麼一個租借例子有助于了解:
1、裝飾設計模式:
裝飾設計模式:
在不必改變源代碼基礎上,動态地擴充一個對象的功能。它是通過建立一個包裝對象,也就是包裹真實的對象。說的直白點,就是對已有對象進行功能增強!
得定義一個類(EmployeeServiceImpl的包裝類):
目的:增強EmployeeServiceImpl中的save和update方法.
做什麼增強:
1:在調用save方法之前:開啟事務:
2:正常調用完save方法之後:送出事務:
3:如果調用save方法出現異常:復原事務:
實作原理:
缺點:劃紅線處
裝飾設計模式在這裡存在的問題:
1):确實可以這個解決問題.
2):會暴露真實對象--不安全.
3):需要為每一個需要增強的類定義一個包裝類.
2、代理模式之靜态代理
而真實生活中有一種房屋中介是這樣的,租客根本就不知道房東是誰,一切簽合同、交租金、交鑰匙等操作都直接和中介公司發生。 我們把這種模式稱之為代理模式。
代理模式:用戶端直接使用的都是代理對象,不知道真實對象是誰,此時代理對象可以在用戶端和真實對象之間起到中介的作用。
1、代理對象完全包含真實對象,用戶端使用的都是代理對象的方法,和真實對象沒有直接關系;
2、代理模式的職責:把不是真實對象該做的事情從真實對象上撇開——職責清晰;
----------------------------------------------------------------------
靜态代理(proxy):在程式運作前就已經存在代理類的位元組碼檔案,代理對象和真實對象的關系在程式運作前就确定了。
解釋:代理類的位元組碼檔案已經産生,這個類是由開發員自己定義的,運作前肯定要先編譯,編譯就會有位元組碼:
動态代理(proxy):動态代理類是在程式運作期間由JVM通過反射等機制動态的生成的,是以不存在代理類的位元組碼檔案。代理對象和真實對象的關系是在程式運作時期才确定的。(後面再講)
靜态代理執行個體:
靜态代理優缺點:
優點:業務類隻需要關注業務邏輯本身,保證了業務類的重用性。
缺點:
1.代理對象的某個接口隻服務于某一種類型的對象,也就是說每一個真實對象都得建立一個代理對象。 (寫死了)
2.如果需要代理的方法很多,則要為每一種方法都進行代理處理。
3.如果接口增加一個方法,除了所有實作類需要實作這個方法外,所有代理類也需要實作此方法。增加了代碼維護的複雜度。
---------------------------
如果期望一個代理類就可以代理多個真實對象------->動态代理.
3、代理模式之動态代理
動态代理類是在程式運作期間由JVM通過反射等機制動态的生成的,是以不存在代理類的位元組碼檔案。代理對象和真實對象的關系是在程式運作事情才确定的。
分别為JDK動态代理、
JDK動态代理API分析:
1、java.lang.reflect.Proxy 類:
public <T>T getProxyObject(){
Object obj=Proxy.newProxyInstance(target.getClass().getClassLoader()//類加載器
, target.getClass().getInterfaces()//表示對象實作的接口
, this);//增強對象
return (T)obj;
}
newProxyInstance:方法職責:為指定類加載器、一組接口及調用處理器生成動态代理類執行個體
loader :類加載器778776
interfaces :模拟的接口
hanlder :代理執行處理器
2.java.lang.reflect.InvocationHandler接口:
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
txManager.begin();
Object ret = null;
try {
ret= method.invoke(target, args);
txManager.commint();
} catch (Exception e) {
txManager.rollback();
}
return ret;
}
proxy :生成的代理對象
method :目前調用的真實方法對象
args :目前調用方法的實參
jdk動态代理操作步驟
① 實作InvocationHandler接口,建立自己增強代碼的處理器。
② 給Proxy類提供ClassLoader對象和代理接口類型數組,建立動态代理對象。
③ 在處理器中實作增強操作。
Notes:
編碼時的一些不注意:
1.這時候配置檔案的時候,一定 要注意:不唯一的bean這種報錯
因為
解決方法:
此時在測試類:
@Autowired
@Qualifier("otherBean")
private IEmployeeService proxy;
2.注入失敗的部分原因:
JDK動态代理存在的問題:
JDK動态代理:
1,代理的對象必須要實作一個接口;
2,需要為每個對象建立代理對象;
3,動态代理的最小機關是類(所有類中的方法都會被處理);
JDK動态代理總結:
1,JAVA動态代理是使用java.lang.reflect包中的Proxy類與InvocationHandler接口這兩個來完成的。
2,要使用JDK動态代理,必須要定義接口。
3,JDK動态代理将會攔截所有pubic的方法(因為隻能調用接口中定義的方法),這樣即使在接口中增加了新的方法,不用修改代碼也會被攔截。
4,如果隻想攔截一部分方法,可以在invoke方法中對要執行的方法名進行判斷。(比如查詢是不用事務處理的)
CGLIB動态代理:
原理是對指定的目标類生成一個子類,并覆寫其中方法實作增強,但因為采用的是繼承,是以不能對final修飾的類進行代理。
public class TransactionxCallBack
implements org.springframework.cglib.proxy.InvocationHandler {
private Object target;//真實對象(被代理的對象--包租婆)
private TransactionManager txManager;
public void setTxManager(TransactionManager txManager) {
this.txManager = txManager;
}
//要提供set方法否則會報未初始化的錯誤
public void setTarget(Object target) {
this.target = target;
}
//建立一個代理對象: 由于使用Object的話待會需要強轉,是以幹脆
public <T>T getProxyObject(){
Enhancer enhancer=new Enhancer();
enhancer.setSuperclass(target.getClass());//設定繼承哪個父類
enhancer.setCallback(this);//增強對象
return (T)enhancer.create(); //建立一個代理對象
}
//在真實對象上放增加操作
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
txManager.begin();
Object ret = null;
try {
ret= method.invoke(target, args);
txManager.commint();
} catch (Exception e) {
txManager.rollback();
}
return ret;
}
}
CGLIB代理總結:
1,CGLIB可以生成目标類的子類,并重寫父類非final修飾符的方法。
2,要求類不能是final的,要攔截的方法要是非final、非static、非private的。
3,動态代理的最小機關是類(所有類中的方法都會被處理);
在Spring中: