先來講說代理模式(靜態代理):
其爲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(); } }