代理模式之靜態代理與動態代理(java動態代理、Cglib動態代理)

目錄

 

一、代理模式

二、靜態代理

三、Java動態代理

四、Cglib動態代理


一、代理模式

在現實生活中,當我們需要去做一些事情,但是自己有沒時間去做,或者自己做的不一定很好,那麼通常就會發一定的代價去請求別人的幫助。像客戶租房子,找中介。明星找經濟人等等。此時中介、經紀人就成了代理商,幫我們做一些事情。

同樣,在開發過程中,我們不想在去重構一些已經封裝好的類,但我們又需要在類的基礎之上加上一些服務。這個時候,我們就可以請求代理者幫我們代理該對象,需要訪問該對象的類,現在只能通過訪問代理類,而達到當下的目的。

代理類並不真正實現業務處理,所謂業務處理的實現是代理類調用委託類實現的,而代理類真正所做的是在業務處理的基礎上添加一些服務,像日誌記錄、權限校驗等等服務。

二、靜態代理

靜態代理實現的前提條件是,與委託類同實現一個接口,意思是說,委託類實現的接口,代理類也要實現。如果,委託類沒有接口,則要創建接口,接口裏的方法必須是委託類中的所有方法,這樣才能幫委託類實現靜態代理。

【案例】:

父接口:

public interface UserService {
	public void query();
	public void update();
}

委託類:

public class UserServiceImpl implements UserService{
	@Override
	public void query() {
		System.out.println("查詢信息.....");
	}
	@Override
	public void update() {
		System.out.println("更新信息.....");
	}
}

代理類:

public class UserServiceImpl implements UserService{
	@Override
	public void query() {
		System.out.println("查詢信息.....");
	}
	@Override
	public void update() {
		System.out.println("更新信息.....");
	}
}

測試方法:

@Test
	public void test01(){
		UserService userService = new UserServiceImplStatic();
		userService.query();
	}

以後凡是用到UserServiceImpl類中的方法,則不能直接使用UserServiceImpl方法,應該去找它的代理對象,從而實現對一些方法的添加的附加服務。

靜態代理方法的優缺點

優點:結構清晰 易於理解
缺點:如果被代理者有多個方法,則代理者也需要開發多個方法,其中,往往存在大量重複代碼,仍然存在代碼重複。

三、Java動態代理

在JDK中提供了動態代理實現的工具類,直接使用該工具類就可以創建出代理者。該工具類實現代理的原理是實現委託類的接口的方法。從而實現對委託類的增加或修改。

調用Proxy.newProxyInstance()創建代理對象。

仍然以上述的父接口與委託類爲案例

代理類:

public class UserServiceJavaProxy {

	private UserService userService = new UserServiceImpl();
	public UserService getProxy(){
		
		UserService proxy = (UserService)Proxy.newProxyInstance(
				/**
				 *【1】classLoader:用來生成代理者類的類加載器,通常可以傳入委託者的類加載器
				 *【2】interfaces: 要求生成的代理者實現的接口們,通常就是實現和委託者相同的接口,
				  保證具有和被代理者相同的方法
				 *【3】invocationHandler: 用來設定回調函數的回調接口,使用者需要寫一個類實現此接口,
				 從而實現其中的invoke方法,在其中編寫代碼處理代理者調用方法時的回調過程,
				 通常在這裏調用真正對象身上的方法, 並且在方法之前或之後做額外操作。
				 */
				userService.getClass().getClassLoader(),
				userService.getClass().getInterfaces(),
				new InvocationHandler() {
					@Override
					public Object invoke(Object obj, Method method, Object[] args) throws Throwable {
						/**
						 * obj是代理對象
						 * method是方法對象,可以通過它來獲取關於方法的信息,方法的名稱,方法執行,方法變量
						 * args是方法的參數列表
						 */
						System.out.println("日誌記錄...");
						//method.invoke(userService, args);相當於userService.method(args);
						Object object = method.invoke(userService, args);
						return object;
					}
				});
		
		return proxy;
	}
}

測試方法:

@Test
	public void test02(){
		UserServiceJavaProxy proxy = new UserServiceJavaProxy();
		UserService userService = proxy.getProxy();
		userService.query();
	}

分析:

JDK動態代理的優缺點

優點:不需要像靜態代理一樣被代理方法都要實現一遍,而只需要在回調函數中進行處理就可以了,重複代碼只需編寫一次。

缺點:java的動態代理是通過代理者實現和委託者相同的接口,來保證兩者具有相同的方法的。(userService.getClass().getInterfaces()就是爲了實現相同的接口)。如果被代理者想要被代理的方法不屬於任何接口,則生成的代理者自然無法具有這個方法,也就無法實現對該方法的代理。所以,java的動態代理機制是基於接口進行的受制於要代理的方法是否有接口的支持。

大家請參照代碼上的註釋。

四、Cglib動態代理

Cglib動態代理是通過繼承委託類的方式,對委託類進行方法的增強。它克服了JDK動態代理實現接口的侷限性,同樣又保留了其優點。

實現動態代理

  1. 創建Enhancer增強器
  2. 實現委託類的接口
  3. 繼承委託類
  4. 設置回調函數,並傳入MethodInterceptor對象,實現其中intercept方法
  5. 生成代理對象

使用Cglib實現動態代理,需要先導入其jar包,纔可以使用。(大家可以,自尋百度)

public class UserServiceCglibProxy {

	private UserService userService = new UserServiceImpl();
	public UserService getProxy(){
		//創建增強器
		Enhancer enhancer = new Enhancer();
		
		//實現委託類的接口, 此方法要求生成的動態代理額外實現指定接口們 ,
		//cglib動態代理不是靠接口實現的,所以可以不設置
		enhancer.setInterfaces(userService.getClass().getInterfaces());
		
		//繼承代理類,此處要傳入被代理者的類,
		//cglib是通過繼承被代理者的類來持有和被代理者相同的方法的,此方法必須設置
		enhancer.setSuperclass(userService.getClass());
		
		//爲增強器設定回調函數,之後通過增強器生成的代理對象調用任何方法都會走到此回調函數中,
		//實現調用真正被代理對象的方法的效果
		enhancer.setCallback(new MethodInterceptor() {
			//重寫intercept方法,對我們需要的方法進行重構
			/**
			 * Object obj,	代理對象
			 * Method method, 需要重構的方法對象 
			 * Object[] args, 方法的參數 
			 * MethodProxy arg3 方法的代理對象
			 */
			@Override
			public Object intercept(Object obj, Method method, Object[] args, MethodProxy arg3) throws Throwable {
				System.out.println("日誌記錄");
				Object object = method.invoke(userService, args);
				return object;
			}
		});
		//生成代理對象
		UserService proxy = (UserService) enhancer.create();
		return proxy;
	}
}

測試方法:

@Test
	public void test03(){
		UserServiceCglibProxy proxy = new UserServiceCglibProxy();
		UserService userService = proxy.getProxy();
		userService.query();
	}

Cglib動態代理優缺點

優點:無論是否有接口都可以實現動態代理,使用場景基本不受限
缺點:第三方提供的動態代理機制,不是原生的,需要導入第三方開發包纔可以使用。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章