zoukankan      html  css  js  c++  java
  • Mybatis那些事-拦截器(Plugin+Interceptor)

    作者:yhjyumi的专栏

    数据权限实现(Mybatis拦截器+JSqlParser)

     Mybatis的拦截器实现机制,使用的是JDK的InvocationHandler.

    当我们调用ParameterHandler,ResultSetHandler,StatementHandler,Executor的对象的时候,
    实际上使用的是Plugin这个代理类的对象,这个类实现了InvocationHandler接口.
    接下来我们就知道了,在调用上述被代理类的方法的时候,就会执行Plugin的invoke方法.
    Plugin在invoke方法中根据@Intercepts的配置信息(方法名,参数等)动态判断是否需要拦截该方法.
    再然后使用需要拦截的方法Method封装成Invocation,并调用Interceptor的proceed方法.
    这样我们就达到了拦截目标方法的结果.
    例如Executor的执行大概是这样的流程:
    拦截器代理类对象->拦截器->目标方法
    Executor->Plugin->Interceptor->Invocation
    Executor.Method->Plugin.invoke->Interceptor.intercept->Invocation.proceed->method.invoke



    注解
    @Intercepts 在实现Interceptor接口的类声明,使该类注册成为拦截器
      Signature[] value//定义需要拦截哪些类,哪些方法
    @Signature 定义哪些类(4种),方法,参数需要被拦截
      Class<?> type()//ParameterHandler,ResultSetHandler,StatementHandler,Executor
      String method()//
      Class<?>[] args()//
    
    接口
    Interceptor 实现拦截器的接口
    
    类
    InterceptorChain 拦截器链,保存了Mybatis配置的所有拦截器,保存在Configuration
      List<Interceptor> interceptors//拦截器
    
    Invocation 类方法的一个封装,在拦截器中就是被调用的目标方法
      Object target//调用的对象
      Method method//调用的方法
      Object[] args//参数
    
    Plugin 插件,其实就是ParameterHandler,ResultSetHandler,StatementHandler,Executor的代理类(Mybatis使用的JDK代理实现拦截器),实现了InvocationHandler接口
     Object target//被代理的目标对象
     Interceptor interceptor//拦截器
     Map<Class<?>, Set<Method>> signatureMap//接口需要拦截的方法(一对多,每个接口对应多个方法)
     //
     wrap(Object target, Interceptor interceptor)//把拦截器对象封装成Plugin代理对象.
     invoke(Object proxy, Method method, Object[] args)//

    使用例子

    1.写一个类,并且实现Interceptor接口
    2.在上述类使用@Intercepts注解,配置拦截信息
    3.在配置文件配置插件

    @Intercepts({ @Signature(type = Executor.class, method = "query", args = { MappedStatement.class, Object.class,
            RowBounds.class, ResultHandler.class }) })
    public class MyInterceptor implements Interceptor {
        @Override
        public Object intercept(Invocation invocation) throws Throwable {
            return invocation.proceed();
        }
        @Override
        public Object plugin(Object target) {
            return Plugin.wrap(target, this);
        }
        @Override
        public void setProperties(Properties properties) {
            // TODO Auto-generated method stub
    
        }
    }

    配置文件加入

    <plugins>
        <plugin interceptor="weber.mybatis.plugin.MyInterceptor" />
    </plugins>
    1. public static void main(String[] args) throws IOException {  
    2.   
    3.         String resource = "conf.xml";  
    4.   
    5.         InputStream is = Test1.class.getClassLoader().getResourceAsStream(resource);  
    6.   
    7.         SqlSessionFactory sessionFactory = new SqlSessionFactoryBuilder().build(is);  
    8.   
    9.         SqlSession session = sessionFactory.openSession();  
    10.   
    11.         // User user = session.selectOne("weber.mybatis.mapper.getUser", 1);  
    12.         User userObj = new User();  
    13.         userObj.setId(123);  
    14.         session.selectList("weber.mybatis.mapper.searchByUser", userObj);  
    15.   
    16.         // UserMapper mapper = session.getMapper(UserMapper.class);  
    17.   
    18.         // mapper.selectAll();  
    19.   
    20.         // System.out.println(user);  
    21.     }  

    开启debug模式,我们将看到Plugin是如何实现代理的.

    当代码执行到下图红色部分的时候

    将直接跳到下图!所以,我们此时的executor不是CachingExecutor对象,而是Plugin代理对象.

    此时的method,就是被调用的目标方法如下:

    最后附上源码注释

    [java] view plain copy
     
    1. /** 
    2.  * @author Clinton Begin 
    3.  */  
    4. //Plugin是JDK动态代理类  
    5. public class Plugin implements InvocationHandler {  
    6.   
    7.   private Object target;//目标对象(ParameterHandler,ResultSetHandler,StatementHandler,Executor)  
    8.   private Interceptor interceptor;//被代理的拦截器  
    9.   //目标类需要拦截的方法缓存.因为一个拦截器可以拦截多个类,一个类可以拦截多个方法.  
    10.   //所以用Map + Set的数据结构存储  
    11.   private Map<Class<?>, Set<Method>> signatureMap;//保存每个拦截器的@signature的配置信息  
    12.   
    13.   private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {  
    14.     this.target = target;  
    15.     this.interceptor = interceptor;  
    16.     this.signatureMap = signatureMap;  
    17.   }  
    18.   //把目标对象和拦截器封装成Plugin代理类实例.  
    19.   public static Object wrap(Object target, Interceptor interceptor) {  
    20.     Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);//获取拦截器的拦截信息(需要拦截的类和方法)  
    21.     Class<?> type = target.getClass();  
    22.     Class<?>[] interfaces = getAllInterfaces(type, signatureMap);//Proxy代理只能代理接口  
    23.     if (interfaces.length > 0) {  
    24.       return Proxy.newProxyInstance(  
    25.           type.getClassLoader(),  
    26.           interfaces,  
    27.           new Plugin(target, interceptor, signatureMap));//Plugin作为代理类,但是实际业务是由Interceptor拦截器完成的.  
    28.     }  
    29.     return target;  
    30.   }  
    31.   
    32.   @Override  
    33.    //proxy,类代理的对象,例如CachingExecutor对象  
    34.   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
    35.     try {//从这里代码看到,会拦截所有的Executor方法,动态的去判断拦截器要不要去拦截.所以要小心使用拦截器,会影响性能.  
    36.       Set<Method> methods = signatureMap.get(method.getDeclaringClass());  
    37.       if (methods != null && methods.contains(method)) {  
    38.         //Invocation是目标对象,目标对象需要拦截的方法,我拦截方法的参数的封装.  
    39.         return interceptor.intercept(new Invocation(target, method, args));//调用拦截器实现拦截  
    40.       }  
    41.       return method.invoke(target, args);//不需要拦截的方法直接放行  
    42.     } catch (Exception e) {  
    43.       throw ExceptionUtil.unwrapThrowable(e);  
    44.     }  
    45.   }  
    46.   
    47.   private static Map<Class<?>, Set<Method>> getSignatureMap(Interceptor interceptor) {  
    48.     Intercepts interceptsAnnotation = interceptor.getClass().getAnnotation(Intercepts.class);//获取拦截器注解@Signature  
    49.     // issue #251  
    50.     if (interceptsAnnotation == null) {  
    51.       throw new PluginException("No @Intercepts annotation was found in interceptor " + interceptor.getClass().getName());        
    52.     }  
    53.     Signature[] sigs = interceptsAnnotation.value();//一个Signature表示一个拦截类型  
    54.     //保存需要拦截类的信息,class作为key, 需要拦截类的方法作为value集合Set保存.一个拦截器可以拦截一个类中多个方法  
    55.     Map<Class<?>, Set<Method>> signatureMap = new HashMap<Class<?>, Set<Method>>();  
    56.     for (Signature sig : sigs) {  
    57.       Set<Method> methods = signatureMap.get(sig.type());  
    58.       if (methods == null) {  
    59.         methods = new HashSet<Method>();  
    60.         signatureMap.put(sig.type(), methods);  
    61.       }  
    62.       try {  
    63.         Method method = sig.type().getMethod(sig.method(), sig.args());//获取需要拦截的方法  
    64.         methods.add(method);  
    65.       } catch (NoSuchMethodException e) {  
    66.         throw new PluginException("Could not find method on " + sig.type() + " named " + sig.method() + ". Cause: " + e, e);  
    67.       }  
    68.     }  
    69.     return signatureMap;  
    70.   }  
    71.   
    72.   private static Class<?>[] getAllInterfaces(Class<?> type, Map<Class<?>, Set<Method>> signatureMap) {  
    73.     Set<Class<?>> interfaces = new HashSet<Class<?>>();  
    74.     while (type != null) {  
    75.       for (Class<?> c : type.getInterfaces()) {  
    76.         if (signatureMap.containsKey(c)) {  
    77.           interfaces.add(c);  
    78.         }  
    79.       }  
    80.       type = type.getSuperclass();  
    81.     }  
    82.     return interfaces.toArray(new Class<?>[interfaces.size()]);  
    83.   }  
    84.   
    85. }  

     

     
    1. /** 
    2.  * @author Clinton Begin 
    3.  */  
    4. //InterceptorChain里保存了所有的拦截器,它在mybatis初始化的时候创建。存在Configuration中  
    5. public class InterceptorChain {  
    6.   
    7.   private final List<Interceptor> interceptors = new ArrayList<Interceptor>();  
    8.   
    9.   //每一个拦截器对目标类都进行一次代理(也就是会出现代理的代理的代理.....有点拗口)  
    10.   public Object pluginAll(Object target) {  
    11.     for (Interceptor interceptor : interceptors) {  
    12.       target = interceptor.plugin(target);  
    13.     }  
    14.     return target;  
    15.   }  
    16.   
    17.   public void addInterceptor(Interceptor interceptor) {  
    18.     interceptors.add(interceptor);  
    19.   }  
    20.     
    21.   public List<Interceptor> getInterceptors() {  
    22.     return Collections.unmodifiableList(interceptors);  
    23.   }  
    24.   
    25. }  

     

     
      1. /** 
      2.  * @author Clinton Begin 
      3.  */  
      4. public interface Interceptor {  
      5.   
      6.   Object intercept(Invocation invocation) throws Throwable;//拦截方法,在这里处理拦截器的业务逻辑  
      7.   
      8.   Object plugin(Object target);//把目标对象封装成Plugin对象  
      9.   
      10.   void setProperties(Properties properties);  
      11.   
      12. }  
  • 相关阅读:
    Ubuntu配置sublime text 3的c编译环境
    ORA-01078错误举例:SID的大写和小写错误
    linux下多进程的文件拷贝与进程相关的一些基础知识
    ASM(四) 利用Method 组件动态注入方法逻辑
    基于Redis的三种分布式爬虫策略
    Go语言并发编程总结
    POJ2406 Power Strings 【KMP】
    nyoj 会场安排问题
    Server Tomcat v7.0 Server at localhost was unable to start within 45 seconds. If the server requires more time, try increasing the timeout in the server editor.
    Java的String、StringBuffer和StringBuilder的区别
  • 原文地址:https://www.cnblogs.com/lxl57610/p/7436980.html
Copyright © 2011-2022 走看看