再寫完《走進Spring中Bean的世界》,寫這篇文章時,本來想法是把AOP的思想、原理以及源碼通通囊入其中,最後發現會和上篇文章同樣,讀起來難以專一。因此仍是本着跳出看全景,轉進去看本質的原則,將AOP的文章分爲三部分,分別爲思想篇、源碼篇、應用篇。html
虛擬棧是一個後入先出(LIFO)棧。每個線程建立時,JVM會爲這個線程建立一個私有的虛擬機棧,當線程調用某個對象的方法時,JVM會相應地建立一個棧幀(Stack Frame)放到虛擬機中,用來表示某個方法的調用。線程對方法的調用就對應一個棧幀的入棧和出棧的過程。spring
public class TestMethod {
public void method1() {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
}
public static void main(String[] args) {
TestMethod target = new TestMethod();
target.method1();
}
}
複製代碼
上面代碼咱們在method()1時,打個斷點。
上面把程序執行機制以鏈接點的概念進行了抽象,下面來了解下面向切面以此爲基準的編程思想express
上面瞭解到AOP是對方法調用進行編程,那麼AOP如何捕獲方法的調用的?其實AOP實現的基礎是代理模式的應用。編程
代理模式中,由抽象角色(Subject)、真實角色(TargetSubject)、代理角色(Proxy)。其中:抽象角色(Subject)負責定義真實角色(TargetSubject)和代理角色(Proxy)應該實現的接口;真實角色(TargetSubject)來完成真正的request功能;代理角色(Proxy)負責將自身的request請求調用真實角色(TargetSubject)對應的request功能來實現業務功能,本身不真正作業務,並在調用request先後,插入代碼實現具體功能。bash
public interface Subject {
public void request();
}
public class TargetSubject implements Subject {
@Override
public void request() {
System.out.println("TargetSubject#request...");
}
}
public class InvocationHandlerImpl implements InvocationHandler {
private TargetSubject targetSubject;
public InvocationHandlerImpl(TargetSubject targetSubject) {
this.targetSubject = targetSubject;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("TargetSubject#request before...");
method.invoke(targetSubject, null);
System.out.println("TargetSubject#request after...");
return null;
}
}
public class Test001 {
public static void main(String[] args) {
TargetSubject targetSubject = new TargetSubject();
// 1.獲取對應的ClassLoader
ClassLoader classLoader = targetSubject.getClass().getClassLoader();
// 2.獲取targetSubject 所實現的全部接口
Class[] interfaces = targetSubject.getClass().getInterfaces();
// 3.設置一個來自代理傳過來的方法調用請求處理器,處理全部的代理對象上的方法調用
InvocationHandler handler = new InvocationHandlerImpl(targetSubject);
/**
* 4.根據上面提供的信息,建立代理對象 在這個過程當中
* a.JDK會經過根據傳入的參數信息動態地在內存中建立和.class 文件等同的字節碼。
* b.而後根據相應的字節碼轉換成對應的class。
* 而後調用newInstance()建立實例。
*/
Subject proxy = (Subject)Proxy.newProxyInstance(classLoader, interfaces, handler);
proxy.request();
}
}
複製代碼
JDK動態代理提供的生成動態代理類有個特色,真實角色(TargetSubject)必須有實現的定義接口(Subject),而且只能代理該接口定義的方法,因此當某個類沒有實現接口,那麼這個類就不能使用動態代理了。那該如何去解決這種狀況呢?請看cglib代理。ide
public class MethodInterceptorImpl implements MethodInterceptor {
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("TargetSubject#request before...");
methodProxy.invokeSuper(o, args);
System.out.println("TargetSubject#request after...");
return null;
}
}
public class Test002 {
public static void main(String[] args) {
TargetSubject targetSubject = new TargetSubject();
MethodInterceptorImpl methodInterceptor = new MethodInterceptorImpl();
//cglib 中增強器,用來建立動態代理
Enhancer enhancer = new Enhancer();
//設置要建立動態代理的類
enhancer.setSuperclass(targetSubject.getClass());
// 設置回調,這裏至關因而對於代理類上全部方法的調用,都會調用CallBack,而Callback則須要實行intercept()方法進行攔截
enhancer.setCallback(methodInterceptor);
TargetSubject proxy =(TargetSubject)enhancer.create();
proxy.request();
}
}
複製代碼
經過上面兩種代理模式的實現方式來看,有抽象角色存在時選擇JDK也能實現,cglib屬於全能型,有無抽象角色都可。Spring中對於「Bean」生成代理對象時,若是該「Bean」實現某個抽象角色(或者接口定義)則選擇JDK動態代理生成代理對象,某則選用cglib方式生成代理對象。post