Spring 為解耦而生,其中AOP(面向切面編程)是很濃重的一筆。
本文來探討一下AOP實現的原理。
一. 概述
代理模式是常用的java設計模式,他的特征是代理類與委托類有同樣的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事后處理消息等。代理類與委托類之間通常會存在關聯關系,一個代理類的對象與一個委托類的對象關聯,代理類的對象本身並不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。
按照代理的創建時期,代理類可以分為兩種。
- 靜態代理:由程序員創建或特定工具自動生成源代碼,再對其編譯。在程序運行前,代理類的.class文件就已經存在了。
- 動態代理:在程序運行時,運用反射機制動態創建而成。
另外還有Cglib動態代理,它是針對類實現的代理。
二. 靜態代理
1. Count.java
1 /** 2 * 定義一個賬戶接口 3 */ 4 public interface Count { 5 // 查看賬戶方法 6 public void queryCount(); 7 8 // 修改賬戶方法 9 public void updateCount(); 10 }
2. CountImpl.java
1 import net.battier.dao.Count; 2 /** 3 * 委托類(包含業務邏輯) 4 */ 5 public class CountImpl implements Count { 6 7 @Override 8 public void queryCount() { 9 System.out.println("查看賬戶方法..."); 10 11 } 12 13 @Override 14 public void updateCount() { 15 System.out.println("修改賬戶方法..."); 16 } 17 }
3. CountProxy.java
1 import net.battier.dao.Count; 2 /** 3 * 這是一個代理類(增強CountImpl實現類) 4 */ 5 public class CountProxy implements Count { 6 private CountImpl countImpl; 7 /** 8 * 覆蓋默認構造器 9 * 10 * @param countImpl 11 */ 12 public CountProxy(CountImpl countImpl) { 13 this.countImpl = countImpl; 14 } 15 16 @Override 17 public void queryCount() { 18 System.out.println("事務處理之前"); 19 // 調用委托類的方法; 20 countImpl.queryCount(); 21 System.out.println("事務處理之后"); 22 } 23 24 @Override 25 public void updateCount() { 26 System.out.println("事務處理之前"); 27 // 調用委托類的方法; 28 countImpl.updateCount(); 29 System.out.println("事務處理之后"); 30 } 31 }
4. TestCount.java
1 import net.battier.dao.impl.CountImpl; 2 import net.battier.dao.impl.CountProxy; 3 /** 4 *測試Count類 5 */ 6 public class TestCount { 7 public static void main(String[] args) { 8 CountImpl countImpl = new CountImpl(); 9 CountProxy countProxy = new CountProxy(countImpl); 10 countProxy.updateCount(); 11 countProxy.queryCount(); 12 } 13 }
觀察代碼可以發現每一個代理類只能為一個接口服務,這樣一來程序開發中必然會產生過多的代理,而且,所有的代理操作除了調用的方法不一樣之外,其他的操作都一樣,則此時肯定是重復代碼。解決這一問題最好的做法是可以通過一個代理類完成全部的代理功能,那么此時就必須使用動態代理完成。
三. 動態代理
與靜態代理類對照的是動態代理類,動態代理類的字節碼在程序運行時由Java反射機制動態生成,無需程序員手工編寫它的源代碼。動態代理類不僅簡化了編程工作,而且提高了軟件系統的可擴展性,因為Java 反射機制可以生成任意類型的動態代理類。java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動態代理類的能力。
JDK動態代理中包含一個接口和一個類:
1).InvocationHandler接口
1 public interface InvocationHandler { 2 public Object invoke(Object proxy, Method method, Object[] args) 3 throws Throwable; 4 }
參數說明:
Object proxy:指被代理的對象。
Method method:要調用的方法
Object[] args:方法調用時所需要的參數
可以將InvocationHandler接口的子類想象成一個代理的最終操作類,替換掉ProxySubject。
2).Proxy類:
Proxy類是專門完成代理的操作類,可以通過此類為一個或多個接口動態地生成實現類,此類提供了如下的操作方法:
1 public static Object newProxyInstance(ClassLoader loader, 2 Class<?>[] interfaces, 3 InvocationHandler h) 4 throws IllegalArgumentException 5 { 6 /************** 7 *具體內容略去** 8 **************/ 9 }
參數說明:
ClassLoader loader:類加載器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子類實例
Ps:類加載器
在Proxy類中的newProxyInstance()方法中需要一個ClassLoader類的實例,ClassLoader實際上對應的是類加載器,在Java中主要有一下三種類加載器:
- Booststrap ClassLoader:此加載器采用C++編寫,一般開發中是看不到的;
- Extendsion ClassLoader:用來進行擴展類的加載,一般對應的是jre\lib\ext目錄中的類;
- AppClassLoader:(默認)加載classpath指定的類,是最常使用的是一種加載器。
動態代理示例:
1. BookFacade.java
1 public interface BookFacade { 2 public void addBook(); 3 }
2. BookFacadeImpl.java
1 import net.battier.dao.BookFacade; 2 3 public class BookFacadeImpl implements BookFacade { 4 5 @Override 6 public void addBook() { 7 System.out.println("增加圖書方法。。。"); 8 } 9 }
3. BookFacadeProxy.java
1 import java.lang.reflect.InvocationHandler; 2 import java.lang.reflect.Method; 3 import java.lang.reflect.Proxy; 4 /** 5 * JDK動態代理代理類 6 */ 7 public class BookFacadeProxy implements InvocationHandler { 8 private Object target; 9 /** 10 * 綁定委托對象並返回一個代理類 11 * @param target 12 * @return 13 */ 14 public Object bind(Object target) { 15 this.target = target; 16 //取得代理對象 17 return Proxy.newProxyInstance(target.getClass().getClassLoader(), 18 target.getClass().getInterfaces(), this); //要綁定接口(這是一個缺陷,cglib彌補了這一缺陷) 19 } 20 21 @Override 22 /** 23 * 調用方法 24 */ 25 public Object invoke(Object proxy, Method method, Object[] args) 26 throws Throwable { 27 Object result=null; 28 System.out.println("事物開始"); 29 //執行方法 30 result=method.invoke(target, args); 31 System.out.println("事物結束"); 32 return result; 33 } 34 }
4. TestProxy.java
1 import net.battier.dao.BookFacade; 2 import net.battier.dao.impl.BookFacadeImpl; 3 import net.battier.proxy.BookFacadeProxy; 4 5 public class TestProxy { 6 public static void main(String[] args) { 7 BookFacadeProxy proxy = new BookFacadeProxy(); 8 BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl()); 9 bookProxy.addBook(); 10 } 11 }
但是,JDK的動態代理依靠接口實現,如果有些類並沒有實現接口,則不能使用JDK代理,這就要使用cglib動態代理了。
四. Cglib動態代理
JDK的動態代理機制只能代理實現了接口的類,而不能實現接口的類就不能實現JDK的動態代理,cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現增強,但因為采用的是繼承,所以不能對final修飾的類進行代理。
示例如下:
1. BookFacadeCglib.java
1 public interface BookFacade { 2 public void addBook(); 3 }
2. BookCadeImpl1.java
1 /** 2 * 這個是沒有實現接口的實現類 3 */ 4 public class BookFacadeImpl1 { 5 public void addBook() { 6 System.out.println("增加圖書的普通方法..."); 7 } 8 }
3. BookFacadeProxy.java
1 import net.sf.cglib.proxy.Enhancer; 2 import net.sf.cglib.proxy.MethodInterceptor; 3 import net.sf.cglib.proxy.MethodProxy; 4 /** 5 * 使用cglib動態代理 6 */ 7 public class BookFacadeCglib implements MethodInterceptor { 8 private Object target; 9 10 /** 11 * 創建代理對象 12 * 13 * @param target 14 * @return 15 */ 16 public Object getInstance(Object target) { 17 this.target = target; 18 Enhancer enhancer = new Enhancer(); 19 enhancer.setSuperclass(this.target.getClass()); 20 // 回調方法 21 enhancer.setCallback(this); 22 // 創建代理對象 23 return enhancer.create(); 24 } 25 26 @Override 27 // 回調方法 28 public Object intercept(Object obj, Method method, Object[] args, 29 MethodProxy proxy) throws Throwable { 30 System.out.println("事物開始"); 31 proxy.invokeSuper(obj, args); 32 System.out.println("事物結束"); 33 return null; 34 } 35 }
4. TestCglib.java
1 import net.battier.dao.impl.BookFacadeImpl1; 2 import net.battier.proxy.BookFacadeCglib; 3 4 public class TestCglib { 5 6 public static void main(String[] args) { 7 BookFacadeCglib cglib=new BookFacadeCglib(); 8 BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1()); 9 bookCglib.addBook(); 10 } 11 }
參考:http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html#3460821