JDK動態代理,代理接口沒有實現類,實現動態代理方式
JDK動態代理,代理接口沒有實現類,實現動態代理
JDK代理,代理的是接口,那麼筆者想一想,既然代理的是接口,那如果沒有實現類怎麼辦,能不能代理。答案是可以的,Mybatis就是這樣的。
Mybatis使用JDK動態代理來實現Mapper接口,事先保存好Mapper接口,和接口聲明的方法,返回值,參數類型,然後代理類的方法調用的時候使用MapperMethod這個事先放入方法緩存裡的對象來真實調用功能。
筆者極度簡化瞭一下代碼:
被代理的接口:
public interface Subject2 { String selectById(); }
這個接口可以看成是Mapper接口
代理對象:
public class SubjectProxy2<T> implements InvocationHandler { private Class<T> proxyInterface; //這裡可以維護一個緩存,存這個接口的方法抽象的對象 SubjectProxy2(Class<T> proxyInterface){ this.proxyInterface = proxyInterface; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (method.getName().equals("selectById")){ //String result = (String) method.invoke(proxyInterface,args); //這裡可以得到方法抽象對象來調用真的的查詢方法 System.out.println("selectById調用成功"); } return null; } public T getProxy(){ return (T) Proxy.newProxyInstance(proxyInterface.getClassLoader(),new Class[]{proxyInterface},this); } }
這個代理類使用瞭泛型,說明這個代理類可以代理所有的mapper接口。
那麼接下來測試一下:
public class ProxyTest2 { public static void main(String[] args) { SubjectProxy2<Subject2> subjectProxy2 = new SubjectProxy2(Subject2.class); Subject2 subject2 = subjectProxy2.getProxy(); subject2.selectById(); } }
結果不言而喻。肯定會有相應的輸出
沒有看mybatis源碼的時候,我以為動態代理一定要要有實現類才能代理,但是看瞭優秀的頂級大牛的源碼之後,我才發現,原來還可以這樣。
jdk動態代理為什麼要接口
jdk的動態代理為什麼用接口,內部是什麼原理呢?看瞭幾篇文章貌似都沒講的清楚明白,因此來解釋一下。
先通過一個簡單例子實現功能:
//接口 public interface SayService { void say(String name); } //實現類 public class SayServiceImpl implements SayService{ @Override public void say(String name) { System.out.println(name); } }
然後再自定義一個增強類, 實現InvocationHandler接口:
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; public class WavingInvocationHandler implements InvocationHandler{ private Object target; public void setTarget(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法執行之前!"); Object obj = method.invoke(target, args); System.out.println("方法執行之後!"); return obj; } }
編寫測試方法:
import sun.misc.ProxyGenerator; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.lang.reflect.Proxy; public class Test { public static void main(String[] args) { //接口 SayService sayService = new SayServiceImpl(); //織入類 WavingInvocationHandler handler = new WavingInvocationHandler(); handler.setTarget(sayService); //代理類--增強的對象 SayService s = (SayService) Proxy.newProxyInstance( sayService.getClass().getClassLoader(), sayService.getClass().getInterfaces(), handler); s.say("say()");//執行代理對象完成業務 /** 方法執行之前! say() 方法執行之後! */ //將jdk中生成代理類輸出到本地.Class文件,之後可以通過反編譯軟件打開查看 createProxyClassFile("test12345",sayService.getClass().getInterfaces()); } private static void createProxyClassFile(String name,Class<?> [] interfaces){ byte[] data = ProxyGenerator.generateProxyClass(name,interfaces);//該方法為jdk中生成代理類的核心方法 FileOutputStream out =null; try { out = new FileOutputStream(name+".class"); System.out.println((new File(name)).getAbsolutePath()); out.write(data); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); }finally { if(null!=out) try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }
好奇心強的小夥伴一定看過newProxyInstance方法看過瞭,
裡面的getProxyClass方法創建代理類:
/* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs);
具體是裡面的哪個方法生成的,小夥伴們不用找半天瞭,慢慢debug後會發現,就是上面提到的
ProxyGenerator.generateProxyClass()
通過該方法生成的代理類如下:
通過反編譯查看源碼,一看便知接口的作用
JDK的動態代理是靠多態和反射來實現的,它生成的代理類需要實現你傳入的接口,並通過反射來得到接口的方法對象(下文中的m3),並將此方法對象傳參給增強類(上文中的WavingInvocationHandler類)的invoke方法去執行,從而實現瞭代理功能,故接口是jdk動態代理的核心實現方式,沒有它就無法通過反射找到方法,所以這也是必須有接口的原因。不知道大傢明白否
public final class test12345 extends Proxy implements SayService { //1、該類實現你傳入的接口並實現方法 // 2、通過構造方法傳入你定義的增強類對象 // 3、通過反射該接口,得到接口裡的Method對象並傳參給增強類,然後執行Invoke實現功能 private static Method m1; private static Method m2; private static Method m3; private static Method m0; public test12345(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler);//2.此處的super指向Proxy中的構造方法並賦值(下文的h就是此處的增強類對象), } //略去無關的hashcode和equals方法 public final void say(String paramString) { try { this.h.invoke(this, m3, new Object[]{paramString});//3.此處的h就是InvocationHandler對象,invoke就是你增強類裡的方法,傳入接口的方法和參數並執行你增強類裡的invoke方法,即完成瞭整個操作! } catch (Error | RuntimeException localError) { throw localError; } catch (Throwable localThrowable) { throw new UndeclaredThrowableException(localThrowable); } } static {//1.靜態代碼塊給屬性賦值,初始化接口中的方法對象(主要是下面的m3) 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 = Class.forName("com.chenrui.core.jdk.SayService").getMethod("say", new Class[]{Class.forName("java.lang.String")}); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } catch (NoSuchMethodException localNoSuchMethodException) { throw new NoSuchMethodError(localNoSuchMethodException.getMessage()); } catch (ClassNotFoundException localClassNotFoundException) { throw new NoClassDefFoundError(localClassNotFoundException.getMessage()); } } }
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。
推薦閱讀:
- Java 動態代理你真的懂瞭嗎(動態和代理)
- 超全MyBatis動態代理詳解(絕對幹貨)
- java代理模式(jdk proxy)
- 一文瞭解Java動態代理的原理及實現
- Java結構型模式之代理模式詳解