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等,都大量使用了動態代理技術,有興趣的童鞋可以去閱讀源碼(表示本人也不是看過很多源碼),這裏面用到了很多的設計模式,對提高自己的代碼水平很有幫助。好啦,就說到這裡吧~

喜歡我的小夥伴記得掃描關注呦~