代理模式是一種使用代理對象來執行目標對象的方法並在代理對象中加強目標對象方法的一種設計模式。html
使用代理模式的緣由有:java
代理能夠分爲靜態代理和動態代理,前者更接近代理模式的本質。程序員
本節根據場景和實現的不一樣,依次介紹靜態代理、JDK動態代理、Cglib動態代理(也稱子類代理)以及Spring AOP代理。spring
靜態代理:在程序運行前就已經存在代理類的字節碼文件。數據庫
示例代碼以下:apache
// 共同接口 // public interface ISubject { void doAction(); void byebye(); } // 真實對象(委託類) // public class RealSubject implements ISubject { @Override public void doAction() { System.out.println("Real Action Here!"); } @Override public void byebye() { System.out.println("Wave goodbye!"); } } // 代理對象(代理類) // public class SubjectProxy implements ISubject { private ISubject subject; public SubjectProxy() { // RealSubject實例可根據環境變量、配置等建立不一樣類型的實例(多態) subject = new RealSubject(); // 此處僅簡單地new實例 } @Override public void doAction() { System.out.println(">> doWhatever start"); // 擴展進行額外的功能操做(如鑑權、計時、日誌等) subject.doAction(); System.out.println("doWhatever end <<"); // 擴展進行額外的功能操做(如鑑權、計時、日誌等) } @Override public void byebye() { System.out.println("Say goodbye"); // 改變委託類行爲(例如實現數據庫鏈接池時避免close關閉鏈接) } } // 驗證代碼 // public class StaticProxyDemo { public static void main(String[] args) { SubjectProxy subject = new SubjectProxy(); subject.doAction(); subject.byebye(); } }
執行結果以下:設計模式
>> doWhatever start Real Action Here! doWhatever end << Say goodbye
靜態代理的特色以下:緩存
JDK動態代理所用到的代理對象,在程序運行階段調用到代理類對象時才由JVM真正建立。JVM根據傳進來的業務實現類對象及方法名,在內存中動態地建立一個代理類的class文件並被字節碼引擎執行,而後經過該代理類對象進行方法調用。具體而言,每一個代理類的對象都會關聯一個表示內部處理邏輯的InvocationHandler接口的實現。當使用者調用代理對象所代理的接口中的方法時,這個調用的信息會被傳遞給InvocationHandler的invoke方法。mybatis
示例代碼以下:app
// 業務接口 // public interface ISubject { void doAction(); } // 業務實現類 // public class RealSubject implements ISubject { @Override public void doAction() { System.out.println("Real Action Here!"); } } public class RealSubject2 implements ISubject { @Override public void doAction() { System.out.println("Real Action2 Here!"); } } // 動態代理類 // public class SubjectJdkProxyHandler implements InvocationHandler { private Object realSubject; public SubjectJdkProxyHandler(Object realSubject) { this.realSubject = realSubject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(">> doWhatever start"); // 擴展進行額外的功能操做(如鑑權、計時、日誌等) Object result = method.invoke(realSubject, args); // 執行目標對象方法 System.out.println("doWhatever end <<"); // 擴展進行額外的功能操做(如鑑權、計時、日誌等) return result; } } // 驗證代碼 // public class JdkProxyDemo { public static void main(String[] args) { ISubject subject = (ISubject) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[] {ISubject.class}, // 或RealSubject.class.getInterfaces() new SubjectJdkProxyHandler(new RealSubject())); // RealSubject必須實現Subject接口,不然沒法強轉後調用業務方法 subject.doAction(); // 使用同一個SubjectProxyHandler類,可代理不一樣的類型 ISubject subject2 = (ISubject) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class<?>[] {ISubject.class}, new SubjectJdkProxyHandler(new RealSubject2())); // 可以使用工廠模式建立代理對象 subject2.doAction(); } }
執行結果以下:
>> doWhatever start Real Action Here! doWhatever end << >> doWhatever start Real Action2 Here! doWhatever end <<
JDK動態代理的特色以下:
Cglib(Code Generation Library)是個功能強大、高性能、開源的代碼生成包,它能夠爲沒有實現接口的類提供代理。具體而言,Cglib繼承被代理的類,覆寫其業務方法來實現代理。由於採用繼承機制,因此不能對final修飾的類進行代理。
示例代碼以下:
// 業務實現類 // public class RealSubject { public void doAction() { System.out.println("Real Action Here!"); } } // 動態代理類(實現方法攔截器接口) // // MethodInterceptor接口來自net.sf.cglib.proxy.MethodInterceptor或org.springframework.cglib.proxy.MethodInterceptor public class SubjectMethodInterceptor implements MethodInterceptor { public Object createCglibProxy(Class<?> targetClass) { Enhancer enhancer = new Enhancer(); // 建立加強器,用來建立動態代理類 enhancer.setSuperclass(targetClass); // 爲加強器指定要代理的業務類,即爲生成的代理類指定父類 enhancer.setCallback(this); // 設置回調(對於代理類上全部方法的調用,都會調用CallBack) return enhancer.create(); // 建立動態代理類對象並返回 // 以上語句可簡化爲:return Enhancer.create(targetClass, this); // } @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println(">> doWhatever start"); // 擴展進行額外的功能操做(如鑑權、計時、日誌等) Object result = methodProxy.invokeSuper(proxy, args); System.out.println("doWhatever end <<"); // 擴展進行額外的功能操做(如鑑權、計時、日誌等) return result; } } // 驗證代碼 // public class CglibProxyDemo { // 還可以使用CGLib + JDK InvocationHandler接口實現動態代理 public static Object createCglibProxyWithHandler(Class<?> targetClass) { MethodInterceptor interceptor = null; try { InvocationHandler invocationHandler = new SubjectJdkProxyHandler(targetClass.newInstance()); interceptor = (Object o, Method method, Object[] objects, MethodProxy methodProxy) -> invocationHandler.invoke(o, method, objects); } catch (InstantiationException | IllegalAccessException e) { e.printStackTrace(); } return Enhancer.create(targetClass, interceptor); } public static void main(String[] args) { RealSubject subject = (RealSubject) new SubjectMethodInterceptor().createCglibProxy(RealSubject.class); subject.doAction(); RealSubject subject2 = (RealSubject) createCglibProxyWithHandler(RealSubject.class); subject2.doAction(); } }
執行結果以下:
>> doWhatever start Real Action Here! doWhatever end << >> doWhatever start Real Action Here! doWhatever end <<
Cglib動態代理的特色以下:
示例代碼以下:
public interface ISubject { void doAction(); } public class RealSubject implements ISubject { @Override public void doAction() { System.out.println("Real Action Here!"); } } public class SubjectSpringAopInvoker implements MethodInterceptor { // 來自org.aopalliance.intercept.MethodInterceptor private RealSubject target; public SubjectSpringAopInvoker(RealSubject realSubject) { this.target = realSubject; } @Override public Object invoke(MethodInvocation methodInvocation) throws Throwable { System.out.println(">> doWhatever start"); // 擴展進行額外的功能操做(如鑑權、計時、日誌等) Object result = methodInvocation.getMethod().invoke(this.target, methodInvocation.getArguments()); System.out.println("doWhatever end <<"); // 擴展進行額外的功能操做(如鑑權、計時、日誌等) return result; } } public class SpringAopProxyDemo { public static void main(String[] args) { ISubject proxy = ProxyFactory.getProxy(ISubject.class, new SubjectSpringAopInvoker(new RealSubject())); proxy.doAction(); } }
執行結果以下:
>> doWhatever start Real Action Here! doWhatever end <<
關於Spring實現動態代理的詳細介紹,可參考《Spring學習總結(二)——靜態代理、JDK與CGLIB動態代理、AOP+IoC》一文。