1. 程式人生 > >【java項目實戰】代理模式(Proxy Pattern),靜態代理 VS 動態代理

【java項目實戰】代理模式(Proxy Pattern),靜態代理 VS 動態代理

自己 text 好的 trace use 代理 分類 plproxy this


這篇博文,我們主要以類圖和代碼的形式來對照學習一下靜態代理和動態代理。重點解析各自的優缺點。


定義


代理模式(Proxy Pattern)是對象的結構型模式,代理模式給某一個對象提供了一個代理對象,並由代理對象控制對原對象的引用。


代理模式不會改變原來的接口和行為,僅僅是轉由代理幹某件事,代理能夠控制原來的目標,比如:代理商,代理商僅僅會買東西,但並不會改變行為。不會制造東西。

讓我們通過以下的代碼好好理解一下這句話。


分類


靜態代理和動態代理


靜態代理


靜態代理類圖

技術分享


代碼演示樣例

技術分享



接口

package com.liang.pattern;

public interface UserManager {

	public void addUser(String userId,String userName);
	
	public void delUser(String userId);
	
	public void modifyUser(String userId,String userName);
	
	public String findUser(String userId);
	
}

目標對象

package com.liang.pattern;

public class UserManagerImpl implements UserManager {

	public void addUser(String userId, String userName) {

		try{
			System.out.println("UserManagerImpl.addUser() userId-->>" + userId);
		}catch(Exception e){
			e.printStackTrace();
			
			throw new RuntimeException();
		}
		
		
	}

	public void delUser(String userId) {

		System.out.println("UserManagerImpl.delUser() userId-->>" + userId);
	}

	public String findUser(String userId) {

		System.out.println("UserManagerImpl.findUser() userId-->>" + userId);
		return "於亮";
	}

	public void modifyUser(String userId, String userName) {

		System.out.println("UserManagerImpl.modifyUser() userId-->>" + userId);
	}

}

代理類,我們使用代理對象做一些日誌記錄,我們將簡略的打印信息到控制臺。

package com.liang.pattern;

public class UserManagerImplProxy implements UserManager {

	private UserManager userManager;
	public UserManagerImplProxy(UserManager userManager){
		this.userManager = userManager;
	}
	public void addUser(String userId, String userName) {
                //記錄日誌等操作或打印輸入參數
		System.out.println("start-->>addUser() userId-->>" + userId);
		try{

			userManager.addUser(userId, userName);
                //運行成功。打印成功信息
			System.out.println("success-->>addUser()");
		}catch(Exception e){
			e.printStackTrace();
                //失敗時。打印失敗信息
			System.out.println("error-->>addUser()");
			//throw new RuntimeException();
		}
	}

	public void delUser(String userId) {
		//同上,略
		userManager.delUser(userId);
	}

	public String findUser(String userId) {
		//同上。略
		userManager.findUser(userId);
		return null;
	}

	public void modifyUser(String userId, String userName) {
		//同上。略
		userManager.modifyUser(userId, userName);
		
	}

}

client調用

package com.liang.pattern;

public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		UserManager userManager = new UserManagerImplProxy(new UserManagerImpl());
		userManager.addUser("001","於亮");
	}
} 

輸出結果,此方法運行成功

start-->>addUser() userId-->>001
UserManagerImpl.addUser() userId-->>001
success-->>addUser()

從類圖我們能夠看出。client本來能夠直接和目標對象打交道,代理中間加了一個間接層。他們實現的功能是一樣的,也沒有改變參數。相信大家對上面的類圖和代碼非常熟悉。跟我們平時看別人的博文一樣。沒有不論什麽差別。以下我們看一下靜態代理的優缺點。


優缺點


長處

1、直觀感受,靜態代理是實實在在的存在的,我們自己寫的。

2、在編譯期增加,提前就指定好了誰調用誰,效率高。


缺點

相同,它的長處也成了它致命的缺點。

1、靜態代理非常麻煩。須要大量的代理類

當我們有多個目標對象須要代理時,我就須要建立多個代理類。改變原有的代碼,改的多了就非常有可能出問題,必須要又一次測試。

2、反復的代碼會出如今各個角落裏,違背了一個原則:反復不是好味道

我們應該杜絕一次次的反復。

3、在編譯期增加,系統的靈活性差



我們能夠看到代理類的每一個方法中,都有記錄日誌,運行成功或失敗的代碼,每一個方法都反復了一遍,假設我們須要改動的話,並沒有比不用靜態代理時降低改動的地方。僅僅是不用改動目標類。動態代理非常好的為我們攻克了這個問題。以下我們看一下動態代理。


動態代理


動態代理類圖


技術分享


代碼演示樣例

技術分享


代理類(不明確,就看看凝視吧)

package com.liang.pattern;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 採用JDK動態代理必須實現InvocationHandler接口。採用Proxy類創建對應的代理類
 * @author liang
 *
 */
public class ProxyHandler implements InvocationHandler {

	private Object targetObject;
	/**
	 * 目標的初始化方法。依據目標生成代理類
	 * @param targetObject
	 * @return
	 */
	public Object newProxyInstance(Object targetObject){
		this.targetObject = targetObject;
		//第一個參數,目標的裝載器
		//第二個參數,目標接口,為每一個接口生成代理
		//第三個參數,調用實現了InvocationHandler的對象。當你一調用代理,代理就會調用InvocationHandler的invoke方法
		return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
	}
	
	/**
	 * 反射,這樣你能夠在不知道詳細的類的情況下,依據配置的參數去調用一個類的方法。

在靈活編程的時候很實用。 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //記錄日誌等操作或打印輸入參數 System.out.println("start-->>" + method.getName()); for(int i=0;i<args.length;i++){ //打印調用目標方法的參數 System.out.println(args[i]); } Object ret = null; try{ //調用目標方法 ret = method.invoke(targetObject, args); //運行成功。打印成功信息 System.out.println("success-->>" + method.getName()); }catch(Exception e){ e.printStackTrace(); //失敗時,打印失敗信息 System.out.println("error-->>" + method.getName()); throw e; } return ret; } }


client調用

package com.liang.pattern;

public class Client {

	/**
	 * @param args
	 */
	public static void main(String[] args) {

		ProxyHandler proxyHandler = new ProxyHandler();
		UserManager userManager = (UserManager)proxyHandler.newProxyInstance(new UserManagerImpl());
		
		String name = userManager.findUser("0001");
		System.out.println("client.main-->>" + name);
	}

} 


輸出結果,執行成功

start-->>findUser
0001
UserManagerImpl.findUser() userId-->>0001
success-->>findUser
client.main-->>於亮


接口和目標類,同上,我就不再浪費大家的帶寬了。


優缺點


長處

1、一個動態代理類更加簡單了,能夠解決創建多個靜態代理的麻煩,避免不斷的反復多余的代碼

2、調用目標代碼時,會在方法“執行時”動態的增加,決定你是什麽類型,才調誰,靈活


缺點

1、系統靈活了。可是相比而言,效率減少了,比靜態代理慢一點

2、動態代理比靜態代理在代碼的可讀性上差了一點,不太easy理解

3、JDK動態代理僅僅能對實現了接口的類進行代理


總結


靜態代理VS動態代理,打成了平手,各自有各的獨特之處,均不可取代,在項目中究竟使用哪種代理,沒有最好。僅僅有更合適。



【java項目實戰】代理模式(Proxy Pattern),靜態代理 VS 動態代理