深入理解JDK動態(tài)代理
代理模式的目的是在不修改原有類方法設(shè)計(jì)的基礎(chǔ)上,對方法行為進(jìn)行增強(qiáng)。
為了好理解,舉個實(shí)際場景,我們業(yè)務(wù)場景中經(jīng)常有限流的需求,常規(guī)操作是在需要限流的接口代碼前加入調(diào)用次數(shù)判斷的代碼,但是這樣每個需要限流的方法都需要加,工作量大不說,一方面不好維護(hù),不能很清晰的知道每個接口限流值,另一方面,限流代碼和業(yè)務(wù)代碼堆疊在一起,也影響代碼閱讀。解法是做一套統(tǒng)一限流,一般好點(diǎn)的會有專門的接口限流平臺,配置對應(yīng)的接口名,設(shè)置限流值,直接就可以限流,實(shí)現(xiàn)方式就可以用動態(tài)代理。不修改原接口的實(shí)現(xiàn),對接口進(jìn)行增強(qiáng)。
動態(tài)代理的優(yōu)勢是實(shí)現(xiàn)無侵入式的代碼擴(kuò)展,做方法的增強(qiáng);讓你可以在不用修改源碼的情況下,增強(qiáng)一些方法;在方法的前后你可以做你任何想做的事情(甚至不去執(zhí)行這個方法就可以)。
靜態(tài)代理
既然有動態(tài),那一定有靜態(tài),說下區(qū)別吧,
靜態(tài):最大的區(qū)別是靜態(tài)是編譯期就決定了,在程序運(yùn)行之前,代理類的.class文件已經(jīng)存在了。被代理類是什么,代理類實(shí)現(xiàn)方式。
舉個栗子:
我現(xiàn)在有個接口,是把Json字符串解析成Object 對象,接口如下:
- public interface IProvider {
- Object getData(String json);
- }
接口的實(shí)現(xiàn)類如下:
- public class SimpleProvider implements IProvider {
- @Override
- public Object getData(String json) {
- //解析json 拿到數(shù)據(jù)
- return parseJson(json);
- }
那現(xiàn)在有個需求,需要對 getData 方法做限流,指定用靜態(tài)代理的方式。
需要很簡單,我就直接貼了:
- 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\":{}}");
- }
這就是靜態(tài)代理,代理類(ProviderProxy)實(shí)現(xiàn)和需要做方法增強(qiáng)的被代理類(SimpleProvider)實(shí)現(xiàn)同一個接口(IProvider),方法具體實(shí)現(xiàn)上做增強(qiáng),這里是限流檢查。
動態(tài)代理
Java 動態(tài)代理
- 動態(tài)代理類:在程序運(yùn)行時,通過反射機(jī)制動態(tài)生成。
- 動態(tài)代理類通常代理接口下的所有類。靜態(tài)一般指定某個類代理。
- 動態(tài)代理事先不知道要代理的是什么,只有在運(yùn)行的時候才能確定。靜態(tài)是編譯期確定的。
還是以IProvider 接口為例,同樣是要對 SimpleProvider 做增強(qiáng),如下:
- 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 對象,我們稱之為執(zhí)行者對象
Proxy對象 (通過在ProviderHandler bind方法中使用Proxy.newProxyInstance生成的對象) 我們稱之為代理對象
這三個對象是什么關(guān)系呢?
Proxy是真正的代理類,SimpleProvider是被代理類,ProviderHandler是執(zhí)行方法增強(qiáng)的執(zhí)行者。
我們是為了增強(qiáng)SimpleProvider (被代理對象)的getData方法,就Proxy對象來代理被代理對象的執(zhí)行,Proxy不親自來做這件事,而是交給執(zhí)行者對象ProviderHandler 來實(shí)現(xiàn)增加的目錄,執(zhí)行調(diào)用前的限流校驗(yàn)。
實(shí)際怎么實(shí)現(xiàn)的呢?
newProxyInstance源碼
- public static Object newProxyInstance(ClassLoader loader,
- Class<?>[] interfaces,
- InvocationHandler h)
- throws IllegalArgumentException
- {
- //對 Invocationhandler做判空處理
- Objects.requireNonNull(h);
- //復(fù)制[IProvider接口]
- final Class<?>[] intfs = interfaces.clone();
- //根據(jù)IProvider的類加載器IProvider接口生成了Proxy類,關(guān)鍵:根據(jù)類加載器和接口對象在JVM緩存中生成一個類對象
- Class<?> cl = getProxyClass0(loader, intfs);
- //獲取構(gòu)造器
- final Constructor<?> cons = cl.getConstructor(constructorParams);
- //保存InvocationHandler的引用
- final InvocationHandler ih = h;
- //通過構(gòu)造器實(shí)例化Proxy代理對象
- return cons.newInstance(new Object[]{h});
- }
代碼注釋寫的很清晰。
可能這個地方大家都會疑惑,生成的Proxy對象是怎樣調(diào)用執(zhí)行者的invoke函數(shù)的。
這個地方通過這段代碼將Proxy0的class字節(jié)碼輸出到文件。
- 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 是動態(tài)生成的類,繼承自Proxy,實(shí)現(xiàn)了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());
- }
- }
- }
重點(diǎn)在 return (String)super.h.invoke(this, m3, new Object[]{var1});代碼。
$Proxy0繼承Proxy類,實(shí)現(xiàn)了IProvider接口,所以也有g(shù)etData()函數(shù),而getData函數(shù)調(diào)用的是執(zhí)行者InvocationHandler的invoke方法,m3是通過反射拿到的Method對象,所以看getData調(diào)用invoke傳遞的。三個參數(shù),第一個是Proxy對象,第二個是getData方法對象,第三個是參數(shù)。
總結(jié)一下:
- 動態(tài)代理的本質(zhì)就是,生成一個繼承自Proxy,實(shí)現(xiàn)被代理接口(IProvider)的類 - Proxy0。
- Proxy0 持有InvocationHandler實(shí)例,InvocationHandler 持有SimpleProvider實(shí)例。Proxy0調(diào)用接口 getData方法時,先傳遞給InvocationHandler,InvocationHandler再傳遞給SimpleProvider實(shí)例。
動態(tài)代理實(shí)際上就是幫我們在JVM內(nèi)存中直接重新生成了代理類class和對應(yīng)類對象,然后通過執(zhí)行者InvocationHandler調(diào)用被代理對象SimpleProvider。
Spring AOP中的代理
Spring代理其實(shí)是對JDK動態(tài)代理和CGLIB代理進(jìn)行了封裝,并且引入了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);
- }
- }