設計模式(一) 動態代理初嘗試

zhouyun123發表於2019-02-26
  • 摘要
      之前老是聽說動態代理,一直沒有機會好好看過,現在就把動態代理的實現邏輯和用處整理一下。
    首先提兩個概念,委託類和代理類。委託類就是實際業務邏輯的處理者,代理類是處於請求發起者與委託類之間的角色,所有對委託類的請求都會經過代理類。
    就是委託類將請求處理委託給代理類,代理類可以起到方法攔截、功能增強的作用。
    實現動態代理的方式有很多,現在主流的主要jdk和cglib這兩個。下面就用示例程式碼說明下動態代理的過程,以及用動態代理實現攔截器。
  • 用JDK實現動態代理
      jdk實現動態代理的包是java.lang.reflect.*,jdk實現動態代理有限制,委託類必須要實現介面,所以先要建立一個介面
    public interface MyInterface {
    
        //jdk
        public void tes1();
    
        //cglib
        public void test2(String val);
    
    }

    建立介面實現類

    public class MyTarget implements MyInterface {
    
        //jdk
        @Override
        public void tes1() {
            System.out.println("委託類業務處理!");
        }
    
        //cglib
        @Override
        public void test2(String val) {
            System.out.println("業務處理結果:"+val);
        }
    }

    動態代理邏輯程式碼

    package proxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    public class MyProxyTest implements InvocationHandler {  //繼承InvocationHandler,攔截方法
    
        private Object target=null;
    
        //建構函式
        public MyProxyTest(Object target){
            this.target = target ;
        }
    
        //獲取代理類的方法
        public static Object getProxy(Object target){
            //通過proxy建立代理類,需要三個引數,委託類的類載入器、委託類實現的介面、代理類
            /**
             * 這是jdk的動態代理模式,必須要有介面
             * proxy會把代理類掛載到所有介面下面
             * 如果委託類沒有實現任何介面會有問題,改用CGLIB的enhancer增強類做動態代理
             */
            return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                    target.getClass().getInterfaces(),new MyProxyTest(target));
        }
    
        //攔截方法
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("進入代理邏輯!");
            System.out.println("代理類處理邏輯!");
            Object result=null;
            result = method.invoke(target,args);
            System.out.println("委託類處理完後的邏輯!");
            return result;
        }
    }

      getProxy方法就是繫結委託類和代理類之間關係的方法,這裡繫結的代理類是this當前物件,也就是MyProxyTest 的例項物件,由於實現了InvocationHandler介面,所以在方法呼叫之前都會
    通過invoke方法,在這裡就可以寫功能增強的程式碼。method.invoke(target,args)是通過反射執行委託類的方法。

    測試用例

    public class JunitTest {
        public static void main(String[] args) {
            /**
             * jdk動態代理
             */
            System.out.println("---------jdk動態代理--------");
            //委託類
            MyTarget target = new MyTarget();
            //給委託類繫結代理類
            MyInterface proxy = (MyInterface)MyProxyTest.getProxy(target);
            proxy.tes1();
        }
    }

    執行結果

    ---------jdk動態代理--------
    進入代理邏輯!
    代理類處理邏輯!
    委託類業務處理!
    委託類處理完後的邏輯!

    我們發現進入了代理類的處理邏輯。

  • CGLIB實現動態代理
      CGLIB實現動態代理和JDK大同小異,不過CGLIB是通過建立增強類Enhancer,並且設定Enhancer的委託類和代理類來實現動態代理。CGLIB的委託類不需要實現介面。
    動態代理邏輯
    package proxy;
    
    import org.springframework.cglib.proxy.Enhancer;
    import org.springframework.cglib.proxy.MethodInterceptor;
    import org.springframework.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    public class MyCgLibProxy implements MethodInterceptor {
    
        public Object getProxy(Class target){
            //建立增強類
            Enhancer enhancer = new Enhancer();
            //設定委託類
            enhancer.setSuperclass(target);
            //設定代理類
            enhancer.setCallback(this);
            return enhancer.create();
        }
    
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("進入代理邏輯!");
            System.out.println("代理類處理邏輯!");
            Object result=null;
            result = methodProxy.invokeSuper(o,objects);
            System.out.println("委託類處理完後的邏輯!");
            return result;
        }
    }

    這裡的getProxy接收的不是委託類的例項物件而是委託類。代理類是this,MyCgLibProxy 的例項物件,由於實現了MethodInterceptor攔截器,所以方法呼叫都會經過intercept。

    測試用例

    package proxy;
    
    public class JunitTest {
        public static void main(String[] args) {
            /**
             * cglib動態代理
             */
            System.out.println("---------cglib動態代理--------");
            MyCgLibProxy cgLib = new MyCgLibProxy();
            MyInterface cgLibProxy = (MyInterface)cgLib.getProxy(MyTarget.class);
            cgLibProxy.test2("cglib動態代理");
        }
    }

    執行結果

    ---------cglib動態代理--------
    進入代理邏輯!
    代理類處理邏輯!
    業務處理結果:cglib動態代理
    委託類處理完後的邏輯!

    簡單的實現動態代理的兩種方式就完成了,之後再講解攔截器和責任鏈模式

  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

相關文章