代理模式的目的是在不修改原有類方法設計的基礎上,對方法行為進行增強。
為了好理解,舉個實際場景,我們業務場景中經常有限流的需求,常規操作是在需要限流的接口代碼前加入調用次數判斷的代碼,但是這樣每個需要限流的方法都需要加,工作量大不說,一方面不好維護,不能很清晰的知道每個接口限流值,另一方面,限流代碼和業務代碼堆疊在一起,也影響代碼閱讀。解法是做一套統一限流,一般好點的會有專門的接口限流平臺,配置對應的接口名,設置限流值,直接就可以限流,實現方式就可以用動態代理。不修改原接口的實現,對接口進行增強。
動態代理的優勢是實現無侵入式的代碼擴展,做方法的增強;讓你可以在不用修改源碼的情況下,增強一些方法;在方法的前后你可以做你任何想做的事情(甚至不去執行這個方法就可以)。
靜態代理
既然有動態,那一定有靜態,說下區別吧,
靜態:最大的區別是靜態是編譯期就決定了,在程序運行之前,代理類的.class文件已經存在了。被代理類是什么,代理類實現方式。
舉個栗子:
我現在有個接口,是把Json字符串解析成Object 對象,接口如下:
- public interface IProvider {
- Object getData(String json);
- }
接口的實現類如下:
- public class SimpleProvider implements IProvider {
- @Override
- public Object getData(String json) {
- //解析json 拿到數據
- return parseJson(json);
- }
那現在有個需求,需要對 getData 方法做限流,指定用靜態代理的方式。
需要很簡單,我就直接貼了:
- public class ProviderProxy implements IProvider{
- //持有一個被代理對象的引用(在這里是SimpleProvider)
- IProvider iProvider;
- public StaticProviderProxy(IProvider iProvider){
- this.iProvider = iProvider;
- }
- @Override
- public Object getData(String json) {
- //做限流檢查
- if(callSpeed > flowLimt) {
- //流量超限
- throw FlowLimitException();
- }
- Object object = iProvider.getData(json);
- return object;
- }
- }
- //main
- public static void main(String[] args) {
- IProvider provider = new ProviderProxy(new SimpleProvider());
- provider.getData("{\"data\":{}}");
- }
這就是靜態代理,代理類(ProviderProxy)實現和需要做方法增強的被代理類(SimpleProvider)實現同一個接口(IProvider),方法具體實現上做增強,這里是限流檢查。
動態代理
Java 動態代理
- 動態代理類:在程序運行時,通過反射機制動態生成。
- 動態代理類通常代理接口下的所有類。靜態一般指定某個類代理。
- 動態代理事先不知道要代理的是什么,只有在運行的時候才能確定。靜態是編譯期確定的。
還是以IProvider 接口為例,同樣是要對 SimpleProvider 做增強,如下:
- public class ProviderHandler implements InvocationHandler {
- Object target;
- public Object bind(Object target){
- this.target = target;
- //這里生成了代理對象
- return Proxy.newProxyInstance(target.getClass().getClassLoader(),
- target.getClass().getInterfaces(), this);
- }
- @Override
- public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
- //限流
- flowLimit(args);
- Object obj = method.invoke(target, args);
- //打印日志
- logger.info("print log...");
- return obj;
- }
- }
- //main
- public static void main(String[] args) {
- ProviderHandler providerHandler = new ProviderHandler();
- IProvider iProvider = (IProvider) providerHandler.bind(new SimpleProvider());
- iProvider.getData("weibo.data");
- }
這里有三個對象:
SimpleProvider 對象 , 我們稱之為被代理對象
ProviderHandler 對象,我們稱之為執行者對象
Proxy對象 (通過在ProviderHandler bind方法中使用Proxy.newProxyInstance生成的對象) 我們稱之為代理對象
這三個對象是什么關系呢?
Proxy是真正的代理類,SimpleProvider是被代理類,ProviderHandler是執行方法增強的執行者。
我們是為了增強SimpleProvider (被代理對象)的getData方法,就Proxy對象來代理被代理對象的執行,Proxy不親自來做這件事,而是交給執行者對象ProviderHandler 來實現增加的目錄,執行調用前的限流校驗。
實際怎么實現的呢?
newProxyInstance源碼
- public static Object newProxyInstance(ClassLoader loader,
- Class<?>[] interfaces,
- InvocationHandler h)
- throws IllegalArgumentException
- {
- //對 Invocationhandler做判空處理
- Objects.requireNonNull(h);
- //復制[IProvider接口]
- final Class<?>[] intfs = interfaces.clone();
- //根據IProvider的類加載器IProvider接口生成了Proxy類,關鍵:根據類加載器和接口對象在JVM緩存中生成一個類對象
- Class<?> cl = getProxyClass0(loader, intfs);
- //獲取構造器
- final Constructor<?> cons = cl.getConstructor(constructorParams);
- //保存InvocationHandler的引用
- final InvocationHandler ih = h;
- //通過構造器實例化Proxy代理對象
- return cons.newInstance(new Object[]{h});
- }
代碼注釋寫的很清晰。
可能這個地方大家都會疑惑,生成的Proxy對象是怎樣調用執行者的invoke函數的。
這個地方通過這段代碼將Proxy0的class字節碼輸出到文件。
- byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", WeiboProvider.class.getInterfaces());
- String path = "C:**/IdeaProjects/study/out/production/study/SimpleProxy.class";
- try(FileOutputStream fos = new FileOutputStream(path)) {
- fos.write(classFile);
- fos.flush();
- System.out.println("代理類class文件寫入成功");
- } catch (Exception e) {
- System.out.println("寫文件錯誤");
- }
反編譯Proxy0如下:
- //Proxy0 是動態生成的類,繼承自Proxy,實現了IProvider接口
- public final class $Proxy0 extends Proxy implements IProvider {
- private static Method m1;
- private static Method m2;
- private static Method m3;
- private static Method m0;
- public $Proxy0(InvocationHandler var1) throws {
- super(var1);
- }
- public final boolean equals(Object var1) throws {
- try {
- return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
- } catch (RuntimeException | Error var3) {
- throw var3;
- } catch (Throwable var4) {
- throw new UndeclaredThrowableException(var4);
- }
- }
- public final String toString() throws {
- try {
- return (String)super.h.invoke(this, m2, (Object[])null);
- } catch (RuntimeException | Error var2) {
- throw var2;
- } catch (Throwable var3) {
- throw new UndeclaredThrowableException(var3);
- }
- }
- public final String getData(String var1) throws {
- try {
- //m3就是IProvider 接口的getData方法
- //super.h 是父類java.lang.reflect.Proxy的屬性 InvocationHandler
- return (String)super.h.invoke(this, m3, new Object[]{var1});
- } catch (RuntimeException | Error var3) {
- throw var3;
- } catch (Throwable var4) {
- throw new UndeclaredThrowableException(var4);
- }
- }
- public final int hashCode() throws {
- try {
- return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
- } catch (RuntimeException | Error var2) {
- throw var2;
- } catch (Throwable var3) {
- throw new UndeclaredThrowableException(var3);
- }
- }
- static {
- try {
- m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
- m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
- //m3就是IProvider 接口的getData方法
- m3 = Class.forName("aop.IProvider").getMethod("getData", new Class[]{Class.forName("java.lang.String")});
- m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
- } catch (NoSuchMethodException var2) {
- throw new NoSuchMethodError(var2.getMessage());
- } catch (ClassNotFoundException var3) {
- throw new NoClassDefFoundError(var3.getMessage());
- }
- }
- }
重點在 return (String)super.h.invoke(this, m3, new Object[]{var1});代碼。
$Proxy0繼承Proxy類,實現了IProvider接口,所以也有getData()函數,而getData函數調用的是執行者InvocationHandler的invoke方法,m3是通過反射拿到的Method對象,所以看getData調用invoke傳遞的。三個參數,第一個是Proxy對象,第二個是getData方法對象,第三個是參數。
總結一下:
- 動態代理的本質就是,生成一個繼承自Proxy,實現被代理接口(IProvider)的類 - Proxy0。
- Proxy0 持有InvocationHandler實例,InvocationHandler 持有SimpleProvider實例。Proxy0調用接口 getData方法時,先傳遞給InvocationHandler,InvocationHandler再傳遞給SimpleProvider實例。
動態代理實際上就是幫我們在JVM內存中直接重新生成了代理類class和對應類對象,然后通過執行者InvocationHandler調用被代理對象SimpleProvider。
Spring AOP中的代理
Spring代理其實是對JDK動態代理和CGLIB代理進行了封裝,并且引入了AOP的概念,同時引入了AspectJ中的一些注解:@pointCut @After 等。
- public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
- if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
- Class<?> targetClass = config.getTargetClass();
- if (targetClass == null) {
- throw new AopConfigException("TargetSource cannot determine target class: " +
- "Either an interface or a target is required for proxy creation.");
- }
- // 如果是接口,使用jdk代理
- if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
- return new JdkDynamicAopProxy(config);
- }
- //否則使用cglib
- return new ObjenesisCglibAopProxy(config);
- }
- else {
- return new JdkDynamicAopProxy(config);
- }
- }
原文地址:https://mp.weixin.qq.com/s/34LAnTGhqe7DTYmT1PRQJg