Spring 爲解耦而生,其中AOP(面向切面編程)是很濃重的一筆。AOP(Aspect-Oriented-Programming)指一種程序設計範型,該範型以一種稱爲切面(aspect)的語言構造爲基礎,切面是一種新的模塊化機制,用來描述分散在對象、類或方法中的橫切關注點(crosscutting)。html
說明:Advice在國內不少的書面資料上都被翻譯爲「通知」,但這個翻譯沒法表達其本質,有少許的讀物將這個詞翻譯爲「加強」,這個翻譯是比較爲準確的詮釋,咱們經過AOP將橫切關注功能加到原有的業務邏輯上,這就是對原有的業務邏輯進行的一種加強,這個加強能夠是前置加強,後置加強,返回後加強,拋出異常時加強,和包圍型加強。java
AOP是運用了動態代理。代理模式是GoF提出的23種設計模式中最爲經典的模式之一,代理是對象的結構模式,他給某一個對象提供一個對象代理,並由代理控制對原對象的引用,簡單的說,代理對象能夠完成比原對象更多的職責,當須要爲原對象添加橫切關注功能時,就可使用原對象的代理對象。咱們在打開Office系列的Word文檔時,若是文檔中有插圖,當文檔剛加載時,文檔中的插圖都只是一個虛框的佔位符,等真正用戶翻到某一頁要查看該圖片時,纔會真正加載這張圖片,這其實就是代理模式的使用,代替真正的虛框就是一個虛擬代理,Hibernate的load方法也是返回一個虛擬代理對象,等真正用戶須要訪問對象的屬性時,才向數據庫發出SQL語句。程序員
按照代理建立初期,代理能夠分爲兩種:面試
下面用一個找槍手代考的例子演示代理模式的使用數據庫
package com.test.test2.interfaces;
/**
* @Author: Young
* @QQ: 403353323
* @Date: 2019/4/25 10:35
*/
public interface Candidate {
public void answerTheQuestion();
}
複製代碼
package com.test.test2; import com.test.test2.interfaces.Candidate; /** * @Author: Young * @QQ: 403353323 * @Date: 2019/4/25 10:36 */ public class LazyStudent implements Candidate { private String name; public LazyStudent(String name) { this.name = name; } @Override public void answerTheQuestion() { // 懶學生只能寫出本身名字不會答題 System.out.println("姓名" + name); } } 複製代碼
package com.test.test2; import com.test.test2.interfaces.Candidate; /** * @Author: Young * @QQ: 403353323 * @Date: 2019/4/25 10:40 */ public class Gunman implements Candidate { private Candidate target; //被代理的對象 public Gunman(Candidate target) { this.target = target; } @Override public void answerTheQuestion() { // 搶手要寫上代考學生的名字 target.answerTheQuestion(); // 搶手幫助懶學生答題並交卷 System.out.println("奮筆疾書書寫正確答案"); System.out.println("交卷"); } } 複製代碼
@Test public void testGunman(){ Candidate c = new Gunman(new LazyStudent("王小二")); c.answerTheQuestion(); } 複製代碼
觀察代理能夠發現每個代理類只能爲一個接口服務,這樣一來程序開發中必然會產生不少的代理,並且,全部代理除了調用方法不同以外,其餘操做都同樣,則此時確定是重複代碼,解決這一個問題最好的作法是經過一個代理類完成所有的代理功能,那麼此時就必須使用動態代理了。編程
從JDK 1.3 開始,Java提供了動態技術,動態代理類的字節碼在程序運行時由Java反射機制動態生成,無需程序員手工編寫它的源代碼。動態代理類不只簡化了編程工做,並且提升了軟件系統的可擴展性,由於反射機制能夠生成任意類型的動態代理。設計模式
java.lang.reflect
包中的Proxy類和InvocationHandler接口提供了生成動態代理的能力。數組
JDK動態代理中包含一個接口和一個類:bash
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
複製代碼
參數說明:markdown
參數 | 說明 |
---|---|
Object proxy | 指被代理的對象 |
Method method | 要調用的方法 |
Object[] args | 方法調用時的參數 |
能夠將InvocationHandler接口的子類想象成一個代理的最終操做類,替換掉ProxySubject。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
/**************
*具體內容略去**
**************/
}
複製代碼
參數說明:
源碼中的參數說明
* @param loader the class loader to define the proxy class * @param interfaces the list of interfaces for the proxy class to implement * @param h the invocation handler to dispatch method invocations to 複製代碼
參數 | 說明 |
---|---|
ClassLoader | 類加載器 |
Class<?>[] interfaces | 獲得所有的接口 |
InvocationHandler h | 獲得InvocationHandler接口的子類實例 |
Ps:在Proxy中類的newProxyInstance()
方法中須要一個ClassLoader類的實例,ClassLoader實際上對應的是類加載器。在Java中主要有三種類加載器:
BookFacade.java
public interface BookFacade {
public void addBook();
}
複製代碼
BookFacadeImpl.java
import net.battier.dao.BookFacade; public class BookFacadeImpl implements BookFacade { @Override public void addBook() { System.out.println("增長圖書方法。。。"); } } 複製代碼
BookFacadeProxy.java
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * JDK動態代理代理類 */ public class BookFacadeProxy implements InvocationHandler { private Object target; /** * 綁定委託對象並返回一個代理類 * @param target * @return */ public Object bind(Object target) { this.target = target; //取得代理對象 return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); //要綁定接口(這是一個缺陷,cglib彌補了這一缺陷) } @Override /** * 調用方法 */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result=null; System.out.println("事物開始"); //執行方法 result=method.invoke(target, args); System.out.println("事物結束"); return result; } } 複製代碼
TestProxy.java
import net.battier.dao.BookFacade;
import net.battier.dao.impl.BookFacadeImpl;
import net.battier.proxy.BookFacadeProxy;
public class TestProxy {
public static void main(String[] args) {
BookFacadeProxy proxy = new BookFacadeProxy();
BookFacade bookProxy = (BookFacade) proxy.bind(new BookFacadeImpl());
bookProxy.addBook();
}
}
複製代碼
說明:使用Java的動態代理有一個侷限性就是被代理的類必須實現接口(也就是說只能對該類所實現接口中定義的方法進行代理),雖然面向接口編程是每一個優秀的Java程序員都知道的規則,但現實每每不盡人意,對於沒有實現接口的代理類如何爲其生成代理類呢?繼承繼承是最經典的擴展已有代碼能力的手段。CGLib代理就是這樣實現代理。
CGLib採用很是底層的字節碼生成技術,經過爲一個類建立子類來生成代理類,它彌補了Java動態代理的不足,所以,Spring中的動態代理和CGLib都是建立代理的重要手段,對於實現了接口的類就用動態代理爲其生成代理類,而沒有實現接口的類就用CGLib經過繼承方式爲其建立代理類。但由於採用的是繼承,因此不能對final修飾的類進行代理。
intercept參數列表:
參數 | 說明 |
---|---|
Object obj | 代理的對象 |
Metho method | 表示攔截的方法 |
Object[] args | 數組表示參數列表,基本數據類型須要傳入其包裝類型,如int-->Integer、long-Long、double-->Double |
MethodProxy methodProxy | 表示對方法的代理,invokeSuper方法表示對被代理對象方法的調用 |
package com.test.test2.proxy; /** * @Author: Young * @QQ: 403353323 * @Date: 2019/5/3 21:32 */ public class BookFacadeImpl1 { public void addBook(){ System.out.println("增長圖書的普通方法"); } } 複製代碼
package com.test.test2.proxy; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * @Author: Young * @QQ: 403353323 * @Date: 2019/5/3 21:34 */ /** * CGLib建立動態代理 */ public class BookFacadeProxy implements MethodInterceptor { private Object target; public Object getInstance(Object target){ this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.target.getClass()); // 回調方法 enhancer.setCallback(this); //建立代理對象 return enhancer.create(); } @Override // 回調方法 public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("開始事務"); methodProxy.invokeSuper(obj, args); System.out.println("事務結束"); return null; } } 複製代碼
public class TestCglib {
public static void main(String[] args) {
BookFacadeCglib cglib=new BookFacadeCglib();
BookFacadeImpl1 bookCglib=(BookFacadeImpl1)cglib.getInstance(new BookFacadeImpl1());
bookCglib.addBook();
}
}
複製代碼