天天看點

JSP-Spring4學習筆記(四)裝飾模式、靜态和動态代理

目錄

0、前言:​

1、裝飾設計模式:

2、代理模式之靜态代理

3、代理模式之動态代理

3.1、JDK動态代理:

3.2CGLIB動态代理

在Spring中:

0、前言:

JSP-Spring4學習筆記(四)裝飾模式、靜态和動态代理

對于上一篇文章:

上述例子存在這麼一個問題:

在我們的業務層中每一個業務方法都得處理事務(繁瑣的try-catch).  

在設計上存在兩個很嚴重問題:          

1):責任不分離.業務方法隻需要關心如何完成該業務功能,不需要去關系事務管理/日志管理/權限管理等等.          

2):代碼結構重複.在開發中不要重複代碼,重複就意味着維護成本增大.

引出課堂上的這麼一個租借例子有助于了解:

JSP-Spring4學習筆記(四)裝飾模式、靜态和動态代理

1、裝飾設計模式:

裝飾設計模式:

在不必改變源代碼基礎上,動态地擴充一個對象的功能。它是通過建立一個包裝對象,也就是包裹真實的對象。說的直白點,就是對已有對象進行功能增強!

得定義一個類(EmployeeServiceImpl的包裝類):
 目的:增強EmployeeServiceImpl中的save和update方法.
 做什麼增強:
    1:在調用save方法之前:開啟事務:
    2:正常調用完save方法之後:送出事務:
    3:如果調用save方法出現異常:復原事務:
           

實作原理:

JSP-Spring4學習筆記(四)裝飾模式、靜态和動态代理

缺點:劃紅線處

裝飾設計模式在這裡存在的問題:
  1):确實可以這個解決問題.
  2):會暴露真實對象--不安全.
  3):需要為每一個需要增強的類定義一個包裝類.
           

2、代理模式之靜态代理

而真實生活中有一種房屋中介是這樣的,租客根本就不知道房東是誰,一切簽合同、交租金、交鑰匙等操作都直接和中介公司發生。 我們把這種模式稱之為代理模式。

代理模式:用戶端直接使用的都是代理對象,不知道真實對象是誰,此時代理對象可以在用戶端和真實對象之間起到中介的作用。

1、代理對象完全包含真實對象,用戶端使用的都是代理對象的方法,和真實對象沒有直接關系;

2、代理模式的職責:把不是真實對象該做的事情從真實對象上撇開——職責清晰;

----------------------------------------------------------------------

靜态代理(proxy):在程式運作前就已經存在代理類的位元組碼檔案,代理對象和真實對象的關系在程式運作前就确定了。

解釋:代理類的位元組碼檔案已經産生,這個類是由開發員自己定義的,運作前肯定要先編譯,編譯就會有位元組碼:

動态代理(proxy):動态代理類是在程式運作期間由JVM通過反射等機制動态的生成的,是以不存在代理類的位元組碼檔案。代理對象和真實對象的關系是在程式運作時期才确定的。(後面再講)

靜态代理執行個體:

JSP-Spring4學習筆記(四)裝飾模式、靜态和動态代理

靜态代理優缺點:

優點:業務類隻需要關注業務邏輯本身,保證了業務類的重用性。

缺點:

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這種報錯

JSP-Spring4學習筆記(四)裝飾模式、靜态和動态代理

因為

JSP-Spring4學習筆記(四)裝飾模式、靜态和動态代理

解決方法:

此時在測試類:

@Autowired
@Qualifier("otherBean")
private IEmployeeService proxy;           

2.注入失敗的部分原因:

JSP-Spring4學習筆記(四)裝飾模式、靜态和動态代理
JSP-Spring4學習筆記(四)裝飾模式、靜态和動态代理
JSP-Spring4學習筆記(四)裝飾模式、靜态和動态代理

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中:

繼續閱讀