咱們知道,Spring AOP的主要做用就是不經過修改源代碼的方式、將非核心功能代碼織入來實現對方法的加強。那麼Spring AOP的底層如何實現對方法的加強?實現的關鍵在於使用了代理模式
代理模式的做用就是爲其它對象提供一種代理,以控制對這個對象的訪問,用於解決在直接訪問對象時帶來的各類問題。代理主要分爲兩種方式:靜態代理和動態代理java
靜態代理主要經過將目標類與代理類實現同一個接口,讓代理類持有真實類對象,而後在代理類方法中調用真實類方法,在調用真實類方法的先後添加咱們所須要的功能擴展代碼來達到加強的目的
示例代碼:程序員
/** * 代理類與目標類的共同接口 */ public interface Subject { void request(); void response(); } /** * 目標類 */ public class RealSubject implements Subject { @Override public void request() { System.out.println("執行目標對象的request方法......"); } @Override public void response() { System.out.println("執行目標對象的response方法......"); } } /** * 代理類 */ public class ProxySubject implements Subject { private Subject subject; public ProxySubject(Subject subject) { this.subject = subject; } @Override public void request() { System.out.println("before 前置加強"); subject.request(); System.out.println("after 後置加強"); } @Override public void response() { System.out.println("before 前置加強"); subject.response(); System.out.println("after 後置加強"); } } public class Main { public static void main(String[] args) { //目標對象 Subject realSubject = new RealSubject(); //代理對象 經過構造器注入目標對象 Subject proxySubject = new ProxySubject(realSubject); proxySubject.request(); proxySubject.response(); } }
運行結果:spring
before 前置加強 執行目標對象的request方法...... after 後置加強 before 前置加強 執行目標對象的response方法...... after 後置加強
經過以上的代碼示例,咱們不難發現靜態代理的缺點。假如咱們的Subject接口要增長其它的方法,則ProxySubject代理類也必須同時代理這些新增的方法。同時咱們也看到,request方法和response方法所織入的代碼是同樣的,這會使得代理類中出現大量冗餘的代碼,很是不利於擴展和維護。爲了解決靜態代理的這些缺陷,因而有了動態代理ide
與靜態代理相比,動態代理的代理類不須要程序員本身手動定義,而是在程序運行時動態生成
動態代理能夠分爲JDK動態代理和CgLib動態代理this
JDK動態代理與靜態代理同樣,目標類須要實現一個代理接口,它的開發步驟以下:
1.定義一個java.lang.reflect.InvocationHandler接口的實現類,重寫invoke方法
2.將InvocationHandler對象做爲參數傳入java.lang.reflect.Proxy的newProxyInstance方法中
3.經過調用java.lang.reflect.Proxy的newProxyInstance方法得到動態代理對象
4.經過代理對象調用目標方法
示例代碼:代理
/** * 自定義InvocationHandler的實現類 */ public class JdkProxySubject implements InvocationHandler { private Subject subject; public JdkProxySubject(Subject subject) { this.subject = subject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before 前置通知"); Object result = null; try { result = method.invoke(subject, args); }catch (Exception ex) { System.out.println("ex: " + ex.getMessage()); throw ex; }finally { System.out.println("after 後置通知"); } return result; } } public class Main { public static void main(String[] args) { //獲取InvocationHandler對象 在構造方法中注入目標對象 InvocationHandler handler = new JdkProxySubject(new RealSubject()); //獲取代理類對象 Subject proxySubject = (Subject)Proxy.newProxyInstance(Main.class.getClassLoader(), new Class[]{Subject.class}, handler); //調用目標方法 proxySubject.request(); proxySubject.response(); } }
運行結果:code
before 前置通知 執行目標對象的request方法...... after 後置通知 before 前置通知 執行目標對象的response方法...... after 後置通知
CgLib動態代理的原理是對指定的業務類生成一個子類,並覆蓋其中的業務方法來實現代理。它的開發步驟:
1.定義一個org.springframework.cglib.proxy.MethodInterceptor接口的實現類,重寫intercept方法
2.獲取org.springframework.cglib.proxy.Enhancer類的對象
3.分別調用Enhancer對象的setSuperclass和setCallback方法,使用create方法獲取代理對象
4.經過代理對象調用目標方法
示例代碼:對象
/** * 自定義MethodInterceptor實現類 */ public class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("before 前置通知"); Object result = null; try { result = methodProxy.invokeSuper(obj, args); }catch (Exception ex) { System.out.println("ex: " + ex.getMessage()); throw ex; }finally { System.out.println("after 後置通知"); } return result; } } public class Main { public static void main(String[] args) { //獲取Enhancer 對象 Enhancer enhancer = new Enhancer(); //設置代理類的父類(目標類) enhancer.setSuperclass(RealSubject.class); //設置回調方法 enhancer.setCallback(new MyMethodInterceptor()); //獲取代理對象 Subject proxySubject = (Subject)enhancer.create(); //調用目標方法 proxySubject.request(); proxySubject.response(); } }
運行結果:繼承
before 前置通知 執行目標對象的request方法...... after 後置通知 before 前置通知 執行目標對象的response方法...... after 後置通知
JDK動態代理和CgLib動態代理的主要區別:
JDK動態代理只能針對實現了接口的類的接口方法進行代理
CgLib動態代理基於繼承來實現代理,因此沒法對final類、private方法和static方法實現代理接口
Spring AOP中的代理使用的默認策略是:
若是目標對象實現了接口,則默認採用JDK動態代理
若是目標對象沒有實現接口,則採用CgLib進行動態代理
若是目標對象實現了接口,且強制CgLib代理,則採用CgLib進行動態代理