代理模式、動態代理與AOP

先來講說代理模式(靜態代理):
其爲23種設計模式之一,屬於結構型模式,其主要思想是經過一個代理對象來代替真實對象來響應client的調用或請求。靜態代理要求代理類與真實類實現一個共同的接口,這樣代理對象才能在「型」上代替真實對象。類圖以下:
java


一個經過代理模式來代理並加強真實對象的簡單示例:node

//定義代理類與真實類的公共接口
public interface Subject {
	public void operation();
}

//真實主題,即被代理對象的類
public class RealSubject implements Subject {
	public void operation() {
		System.out.println("執行業務處理... ...");
	}
}

//使用代理對象來代替真實對象,並對真實對象中的處理方法進行加強
public class ProxySubject implements Subject {
	private Subject real;                        //持有真實對象
	
	public ProxySubject(Subject subject){        //constructor,注入真實的被代理的對象
		this.real = subject;
	}
	
	public void operation() {                    //對被代理的對象方法進行加強
		beforeOperation();
		real.operation();                    //真實主題中的方法
		afterOperation();
	}
	
	private void beforeOperation(){                 //加強處理
		System.out.println("業務預先處理...");
	}
	private void afterOperation(){                  //加強處理
		System.out.println("業務善後處理...");
	}

}

//測試main函數
public class Main {
	public static void main(String[] args) {
		Subject real = new RealSubject();
		Subject proxy = new ProxySubject(real);
		proxy.operation();                       //在實際使用時,操做代理對象來代替真實對象
	}
}

代理模式應用很是普遍,在各類框架中也能找到其應用。例如在DAO(data access object)模式中:
DAO接口:聲明要對DB進行哪些操做
DAOImpl類:實現DAO接口,定義對DB的具體操做
DAOProxy類:代理DAOImpl,並額外進行DB的打開、關閉操做
DAOFactory:建立代理對象的工廠
spring

然而靜態代理並非很靈活,如上例,就只能代理和加強實現了Subject接口的真實類,不能代理任意類型的真實類。設計模式

 

下面來看動態代理
動態代理便可以在運行時生成代理,其更加靈活,Java提供了Proxy類和InvocationHandler接口兩個工具來實現動態代理功能。動態代理的流程以下圖:
api


使用java api中靜態方法:Proxy.newProxyInstance()來生成對象:
Proxy.newProxyInstance(classloader, interface, handler)  => proxy對象
classloader:類加載器,用於將動態生成的代理類的字節碼加載到JVM中,通常就使用被代理類的加載器
interface:被代理類實現的接口,故此,當被代理中存在非接口的方法時,應該就沒法被動態加強
handler:InvocationHandler接口的實現類,在handler.invoke()中實現對被代理類接口方法進行加強
框架

動態代理示例:dom

//真實類的接口型
public interface Subject {
	public void operation();                    //定義了真實類的操做
}

//真實類:
public class RealSubject implements Subject {      //實現接口,完成業務處理
	public void operation() {
		System.out.println("執行業務處理... ...");
	}
}

//加強工具:注意此工具沒有實現Subject接口,徹底是一個獨立的工具方法單元,通常被稱爲攔截器
public class EnhanceTool {
	public void beforeOperation(){                  //加強處理
		System.out.println("業務預先處理...");
	}
	public void afterOperation(){                   //加強處理
		System.out.println("業務善後處理...");
	}
}

//***動態代理的核心之處***
public class MyInvocationHandler implements InvocationHandler {
	private Object real;
	public void setReal(Object real){              //持有被代理的對象
		this.real = real;
	}
                                      //InvocationHandler接口中的方法,在此方法中對被代理類接口方法加強
	public Object invoke(Object proxy, Method method, Object[] args)throws Throwable {
		EnhanceTool tool = new EnhanceTool();
		tool.beforeOperation();                       //加強工具方法
		Object result = method.invoke(real, args);    //經過反射調用真實對象中的方法
		tool.afterOperation();                        //加強工具方法
		return result;
	}
}

//生成代理對象的工廠:
public class MyProxyFactory {
	public static Object getProxy(Object real) throws Exception{
		MyInvocationHandler handler = new MyInvocationHandler();
		handler.setReal(real);

                //Proxy中的靜態方法:輸入參數(類加載器,被代理類的接口,InvocationHandler的實現類),返回代理對象
		return Proxy.newProxyInstance(real.getClass().getClassLoader(), real.getClass().getInterfaces(), handler);
	}
}

//測試main:
	public static void main(String[] args) throws Exception {
		Subject real = new RealSubject();
		Subject proxy = (Subject) MyProxyFactory.getProxy(real);
		proxy.operation();
	}

經過上面的示例可以看到,動態代理更加的靈活,其加強方法的類徹底是一個獨立於代理過程的單元,只是單純的被調用,能隨時被替換。且像生成代理對象或調用真實對象方法的具體操做,都由Java API來提供。可以很是好的實現解耦。函數

接下來是AOP(aspect orient program),下面的示例使用動態代理來模擬spring中的AOP。根據xml文件中的配置信息來肯定動態代理的加強流程和方法。(在時間切面上插入加強方法,來增長總體程序的功能)。工具

//AOP接口,即動態代理中被代理類實現的接口
public interface AOP {
	public void saveMoney();
	public void getMoney();
}

//被代理類,實現接口中的方法
public class AOPImpl implements AOP {
	public void saveMoney() {	
		System.out.println("正在存錢...");
	}
	public void getMoney() {
		System.out.println("正在取錢...");
	}
}
//動態代理中的handler:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

public class AOPHandler implements InvocationHandler {
	private Object obj; 
	private boolean flag;  
	
	public AOPHandler(Object obj){
		this.obj = obj;
	}
	
	public void setFlag(Map<String, String> config) {     //根據解析xml獲得的map,設置標誌位,進而影響invoke中的執行流程
		if (config == null) {
			flag = false;
		} else {
			if (config.containsKey("transaction") && "true".equalsIgnoreCase(config.get("transaction"))) {
				flag = true;
			} else {
				flag = false;
			}
		}
	}
	//關鍵之處,接口方法,當經過代理對象調用真實類中的方法時,實際調用的是下面的invoke方法
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if (flag) {                                //根據配置信息來決定是否執行
            doBefore();  
        }  
        Object result = method.invoke(obj, args);  //根據反射調用被代理類中的方法
        if (flag) {                                //根據配置信息來決定是否執行
            doAfter();  
        }  
        return result;  
	}
	
    private void doBefore() {                           //加強處理,或叫時間切面長插入的方法
        System.out.println("Transaction start...");  
    }  
  
    private void doAfter() {  
        System.out.println("Transaction commit...");  
    } 
    
}
//經過DOM( Document Object Model)來解析xml配置文件
//將xml配置文件中的配置信息,解析爲<key, value>對的形式放入map中
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class XMLUtil {
	public static Map<String, String> parseXML() throws Exception {
		Map<String, String> result = new HashMap<String, String>();
		DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
		DocumentBuilder db = dbf.newDocumentBuilder();

		InputStream in = XMLUtil.class.getClassLoader().getResourceAsStream("config.xml");
		Document document = db.parse(in);
		Element root = document.getDocumentElement();
		NodeList xmlNodes = root.getChildNodes();                        //將整個xml文件轉換爲NodeList形式
		for (int i = 0; i < xmlNodes.getLength(); i++) {                  //遍歷NodeList取得配置信息
			Node config = xmlNodes.item(i);                           //Node封裝了配置的節點名與值的信息
			if (null != config && config.getNodeType() == Node.ELEMENT_NODE) {
				String nodeName = config.getNodeName();
				if ("transaction".equals(nodeName)) {                     //Node.getNodeName()
					String textContent = config.getTextContent();     //Node.getTextContext()
					result.put("transaction", textContent);           //將配置信息放入map中
				}
			}
		}
		return result;
	}
}
//調用解析方法,將xml文件的解析結果放入map中
import java.util.Map;

public class JVMCache {
    private static Map<String, String> config;         //儲存解析xml獲得的配置信息
	
    public synchronized static Map<String, String> getConfig() throws Exception {  
        if (config == null) {  
            config = XMLUtil.parseXML();  
        }  
        return config;  
    } 	
}
/src/config.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<config>
	<transaction>true</transaction>        //此處爲改成fals,則handler.invoke()中的兩個加強方法就不會執行
</config>
//測試主函數:
public class Client {
	public static void main(String[] args) throws Exception {
		AOPImpl impl = new AOPImpl(); 
		AOPHandler handler = new AOPHandler(impl);      //在handler中,持有一個被代理對象
		handler.setFlag(JVMCache.getConfig());          //將xml配置文件解析出來的map傳入handler
		//獲取代理對象
		AOP aop = (AOP) Proxy.newProxyInstance(AOPImpl.class.getClassLoader(), new Class<?>[] {AOP.class}, handler); 
		
		aop.saveMoney();
		aop.getMoney();
	}
}
相關文章
相關標籤/搜索