天天看點

Java攔截器

JDK動态代理

最近也在瘋狂的補基礎知識,是以寫這篇部落格為了友善以後自己檢視。

攔截器

  • 由于動态代理一般都比較難了解,程式設計者會設計一個攔截器接口供開發者使用, 開發者隻要知道攔截器接口的方法、含義和作用即可,無須知道動态代理是怎麼實作的。 用 JDK 動态代理來實作一個攔截器的邏輯,為此先定義攔截器接口
    import java.lang.reflect.Method;
    
     public interface Interceptor {
         /**
          * 當傳回為true時,則反射真實對象的方法;
          * 當傳回為false時,則調用around方法。
          * @param proxy 代理對象
          * @param target    真實對象
          * @param method    方法
          * @param args  運作方法參數
          * @return
          */
         public boolean before(Object proxy, Object target, Method method,Object[] args);
    
         /**
          * 在before方法傳回為false時,則調用around方法。
          * @param proxy
          * @param target
          * @param method
          * @param args
          * @return
          */
         public void around(Object proxy, Object target,Method method,Object[] args);
    
         /**
          * 在反射真實對象方法或者around方法執行之後,調用after方法
          * @param proxy
          * @param target
          * @param method
          * @param args
          * @return
          */
         public void after(Object proxy, Object target,Method method,Object[] args);
     }
               
  • 建立這個Interceptor的實作類-----MyInterceptor
    package com.yl.interceptor;
    
    import java.lang.reflect.Method;
    
     /**
      * 攔截器實作類
      */
     public class Mylnterceptor implements Interceptor{
         @Override
         public boolean before(Object proxy, Object target, Method method, Object[] args) {
             System.out.println("反射方法前邏輯---判斷使用者是否處于登入狀态---使用者未登入,操作攔截");
             return false;   //不反射被代理對象原有方法
         }
    
         @Override
         public void around(Object proxy, Object target, Method method, Object[] args) {
             System.out.println("取代了被代理對象的方法---頁面轉發為指定界面,比如登入界面");
         }
    
         @Override
         public void after(Object proxy, Object target, Method method, Object[] args) {
             System.out.println("反射方法後邏輯---記錄本次異常操作");
         }
     }
               

它實作了所有的 Interceptor 接口方法,使用JDK動态代理,就可以實作這些方法在适當時的調用邏輯

  • 在JDK動态代理中使用攔截器以及test用例
    package com.yl.interceptor;
    
     import com.yl.jdk.HelloWorld;
     import com.yl.jdk.HelloWorldImpl;
    
     import java.lang.reflect.InvocationHandler;
     import java.lang.reflect.Method;
     import java.lang.reflect.Proxy;
    
     /**
      * 在JDK動态代理中使用攔截器
      */
     public class InterceptorJdkProxy implements InvocationHandler {
     
         private Object target = null;   //真實對象
         private String interceptorClass = null;     //攔截器全限定名
     
         public InterceptorJdkProxy(Object target, String interceptorClass) {
             this.target = target;
             this.interceptorClass = interceptorClass;
         }
    
         /**
          * 綁定委托對象并傳回一個【代理占位】
          * @param target    真實對象
          * @param interceptorClass
          * @return  代理對象【占位】
          */
         public static Object bind(Object target,String interceptorClass) {
             //取得代理對象
             return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                     target.getClass().getInterfaces(),
                     new InterceptorJdkProxy(target,interceptorClass));
         }
    
         /**
          * 通過代理對象調用方法,首先進入這個方法
          * @param proxy     代理對象
          * @param method    方法,被調用方法
          * @param args      方法的參數
          * @return
          * @throws Throwable
          */
         @Override
         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     
             if (interceptorClass == null) {
                 //沒有設定攔截器則直接反射原有方法
                 return method.invoke(target,args);
             }
     
             Object result = null;
             //通過反射生成攔截器
             Interceptor interceptor = (Interceptor) Class.forName(interceptorClass).newInstance();
             //調用前置方法
             if (interceptor.before(proxy,target,method,args)) {
                 //傳回true,反射原有對象的方法
                 result = method.invoke(target,args);
             }else {
                 //傳回false,執行around方法
                 interceptor.around(proxy,target,method,args);
             }
    
             //調用後置方法
             interceptor.after(proxy,target,method,args);
             return result;
         }
    
         public static void main(String[] args) {
             //注冊攔截器
             HelloWorld proxy = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(),
                     "com.yl.interceptor.Mylnterceptor");
             //不注冊攔截器
             HelloWorld proxy2 = (HelloWorld) InterceptorJdkProxy.bind(new HelloWorldImpl(),null);
    
             /**
              * 結果
              *  反射方法前邏輯---判斷使用者是否處于登入狀态---使用者未登入,操作攔截
              *  取代了被代理對象的方法---頁面轉發為指定界面,比如登入界面
              *  反射方法後邏輯---記錄本次異常操作
              */
             proxy.sayHelloWorld();
     
             /**
              * 結果
              *  Hello World !
              */
             proxy2.sayHelloWorld();
         }
     }
               
    這裡有兩個屬性,一個是target,它是真實對象;另一個是字元串interceptorClass,它是一個攔截器的權限的類名。這裡解釋一下代碼執行的步驟:
    第 1 步,在 bind 方法中用 JDK 動态代理綁定了一個對象,然後傳回代理對象。 
     	第 2 步, 如果沒有設定攔截器, 則直接反射真實對象的方法,然後結束,否則進行第 3 步。 
     	第 3 步,通過反射生成攔截器,并準備使用它。
     	第 4 步,調用攔截器的 before 方法,如果傳回為 true,反射原來的方法;否則運作攔 截器的 around 方法。 
     	第 5 步,調用攔截器的 after 方法。 
     	第 6 步,傳回結果。
               
Java攔截器
  • HelloWorld接口
    /**
      *  JDK動态代理:必須借助一個接口才能産生代理對象,是以必須先定義接口。
     */
     public interface HelloWorld {
         public void sayHelloWorld();
     }
               
  • 提供HelloWorld的實作類來實作接口
    /**
      * 提供HelloWorld的實作類來實作接口
      */
     public class HelloWorldImpl implements HelloWorld{
         @Override
         public void sayHelloWorld() {
             System.out.println("Hello World !");
         }
     }