java學習筆記(中級篇)—JDK動態代理
- 2019 年 10 月 3 日
- 筆記
一、什麼是代理模式
相信大家都知道代理商這個概念,在商業中,代理商無處不在。假設你要去買東西,你不可能去找真正的廠家去買,也不可能直接跟廠家提出需求,代理商就是這中間的一橋樑,連接買家和廠商。你要買或者定製什麼產品,需要什麼規格樣式和價格的直接跟代理商溝通就好了,由代理商與真實的廠商溝通,這樣買家有什麼問題都可以諮詢代理商,真實廠商也就可以專心做產品,不需要管其他事務了,買家也插手不了廠商的事。
在以上的關係中,廠商就是被代理對象,代理商就是代理對象,而買家就是調用者了。java中有多種動態代理的技術,包括JDK、CGLIB、Javassist等,這裡我會拿CGLIB與JDK動態代理作比較。
二、意義
動態代理的意義就是在與生成一個佔位(代理對象),來代理真實對象,從而達到控制真實對象的目的。要了解動態代理,首先要具備反射的知識。
三、實現動態代理的步驟
代理的實現分為兩個主要步驟:
1.代理對象和真實對象建立代理關係
2.實現代理對象的邏輯方法
四、JDK動態代理
JDK動態代理,JDK自帶的功能,在java.lang.reflect.*包中。要實現JDk動態代理,必須要藉助接口才能產生代理對象。
1.先來定義一個簡單的接口HelloWorld.java
public interface HelloWorld { void sayHello(); }
2.HelloWorld的實現類HelloWorldImpl.java
public class HelloWorldImpl implements HelloWorld { @Override public void sayHello() { System.out.println("hello world"); } }
3.建立代理關係,必須實現接口InvocationHandler。invoke是接口裡唯一需要實現的方法,裏面實現的代理邏輯,當代理對象調度方法時,就會映射到invoke方法來,實際是通過反射來實現的。而bind()方法就是建立代理關係,通過Proxy的newProxyInstance來創建代理對象,第一個參數類加載器,第二個參數下掛的接口,第三個參數則是實現了代理邏輯方法invoke的類。
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JdkProxy implements InvocationHandler { //真實對象 private Object target = null; /** * 建立真實對象與代理對象之間的關係 * @param target 傳入真實對象 * @return */ public Object bind(Object target){ this.target = target; return Proxy.newProxyInstance (target.getClass().getClassLoader(), target.getClass().getInterfaces(),this); } /** * 代理邏輯 * @param proxy 代理對象 * @param method 當前調度的方法 * @param args 調度方法的參數 * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("進入代理邏輯方法"); System.out.println("在調用真實對象之前的服務"); Object object = method.invoke(target,args); System.out.println("在調用真實對象之後的服務"); return object; } }
4.測試
import org.junit.Test; public class JdkProxyTest { @Test public void test_JdkProxy(){ JdkProxy proxy = new JdkProxy(); HelloWorld helloWorld = null; try { helloWorld = (HelloWorld) proxy .bind(Class.forName ("com.xcl.ssm.chapter2.jdkproxy.HelloWorldImpl") .newInstance()); } catch (Exception e) { e.printStackTrace(); } helloWorld.sayHello(); } }
五、CGLIB動態代理
上面說JDK動態代理的實現必須藉助接口才能實現,而CGLIB則不用,只需要有一個非抽象類即可。我們以上面的HelloWorldImpl類為例,現在沒有HelloWorld接口了。JDK動態代理必須要實現invocationHandler接口,CGLIB則必須要實現MethodInterceptor接口,裏面也只有一個需要實現的方法intercept(),需要在裏面實現代理邏輯。寫代碼之前記得導包哦,這是第三方提供的技術。
public class CglibProxy implements MethodInterceptor { /** * 生成CGLIB代理對象 * @return */ public Object getProxy(Class clz){ //CGLIB增強類對象 Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clz); //設置當前對象為代理邏輯對象 enhancer.setCallback(this); //生成並返回代理對象 return enhancer.create(); } /** * 代理邏輯方法 * @param proxy 代理對象 * @param method 方法 * @param args 參數 * @param methodProxy 方法代理 * @return * @throws Throwable */ @Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { System.out.println("調用真實對象前"); //通過反射調用真實對象的方法 Object obj = methodProxy.invokeSuper(proxy,args); System.out.println("調用真實對象後"); return obj; } }
六、總結
要真正的參透動態代理就要非常熟悉反射機制,從以上的代碼可以得知,動態代理的底層實現還是基於反射的,我們知道反射是很消耗性能的,這帶來了一些性能問題,但是為了開發上的簡便,這種犧牲是值得的。我們學的框架,比如spring,mybatis等,都大量使用了動態代理技術,有興趣的童鞋可以去閱讀源碼(表示本人也不是看過很多源碼),這裏面用到了很多的設計模式,對提高自己的代碼水平很有幫助。好啦,就說到這裡吧~
喜歡我的小夥伴記得掃描關注呦~