1. 程式人生 > >動態代理實現日誌的寫入

動態代理實現日誌的寫入

    之前在學習設計模式的時候就學習過代理模式,在DRP的學習過程中,又一次遇到了代理模式,但是這次接觸到的是動態代理。做專案的時候也聽同學們提到過AOP,那麼動態代理和AOP是一種什麼樣的關係呢?

   一、代理定義

                 

                                   圖1  代理模式類圖

    代理模式:給某一個物件提供一個代理,並由代理物件控制對原物件的引用。

    代理模式能夠協調呼叫者和被呼叫這,在一定的程度上降低了系統的耦合度,但是由於在客戶端和真實主題之間增加了代理物件,因此有些型別的代理模式可能會造成請求的處理速度變慢。

   二、動態代理

    動態代理是一種較為高階的代理模式,它的典型應用就是Spring AOP.

    在傳統的代理模式中,客戶端通過Proxy呼叫RealSubject類的request()方法,同時還在代理類中封裝了其他方法(如preRequest()和postRequest()),可以處理一些其他問題,如果按照這種方式使用代理模式,那麼真實主題角色必須是事先已經存在的,並將其作為代理物件的內部成員屬性。如果一個真實主題角色必須對應一個代理主題角色,這將導致系統中類的個數急劇增加,因此需要想辦法減少系統中類的個數,在實現不知道真實主題角色的情況下使用代理主體角色,這就是動態代理需要解決的問題。

三、例項程式碼

    1.主題介面

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);
}

    2.真實主題

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);
	}
}

    3.動態代理

    Java動態代理實現相關類位於java.lang.reflect包,主要涉及兩個物件,InvocationHandler介面和Proxy類。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class LogHandler implements InvocationHandler {
	
	private Object targetObject;
	//Proxy為動態代理類
	public Object newProxyInstance(Object targetObject) {
		this.targetObject = targetObject;
		return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
							   targetObject.getClass().getInterfaces(), this);
	}
	/*
	 * @proxy:表示代理類
	 * @method:表示需要代理的方法
	 * @args:表示代理方法的引數陣列
	 */
	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;
	}
}

    在newProxyInstance方法中,目標主題作為引數,我們通過targetObject.getClass().getClassLoader()獲取ClassLoader物件,然後通過targetObject.getClass().getInterfaces()獲取它實現的所有介面,這樣就在記憶體中建立了一個動態代理物件。但是值得注意的一點是,這個被建立的動態代理類裡面只有目標主題的方法,而沒有其實現,當客戶端呼叫的動態代理類的方法時,需要呼叫一個實現了InvocationHandler介面的類,這樣才能代用InvocationHandler中的invoke()回撥方法,所以要把LogHandler自身作為引數傳入。

    4.客戶端呼叫

public class Client {

	public static void main(String[] args) {
		LogHandler logHandler = new LogHandler();
		UserManager userManager = (UserManager)logHandler.newProxyInstance(new UserManagerImpl());

		String name = userManager.findUser("0001");
		System.out.println("Client.main() --- " + name);
	}
}

    在這裡,userManager作為代理物件,呼叫userManager.findUser()就會呼叫InvocationHandler中的invoke方法,所以在invoke()方法中處理相應的日誌操作即可。

    總結:

    代理模式的進一步深入學習,越發感覺到面向物件程式設計的魅力。invoke()可以聯想到在學習js的回撥函式,所以接收起來並不太陌生。動態代理模式解決了靜態代理模式要求建立多個代理類的缺陷,把編譯時建立的例項延遲到執行時。AOP將日誌記錄,效能統計,安全控制,事務處理,異常處理等程式碼從業務邏輯程式碼中劃分出來,通過對這些行為的分離,我們希望可以將它們獨立到非指導業務邏輯的方法中,進而改變這些行為的時候不影響業務邏輯的程式碼。無疑,代理模式為AOP的實現提供了良好的解決思路。