美文网首页
mybatis拦截器及项目应用

mybatis拦截器及项目应用

作者: cccccttttyyy | 来源:发表于2018-10-18 15:24 被阅读0次

    目录

    拦截器接口
    注册拦截器
    拦截原理
    简单打印的demo
    项目中编写的权限拦截器


    拦截器接口

    接口中的方法

    Mybatis为我们提供了一个Interceptor接口,通过实现该接口就可以定义我们自己的拦截器。我们先来看一下这个接口的定义:

    import java.util.Properties;
     
    public interface Interceptor {
      //当plugin函数返回代理,就可以对其中的方法进行拦截来调用intercept方法
      Object intercept(Invocation invocation) throws Throwable;
      //plugin方法是拦截器用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理。
      Object plugin(Object target);
     //在Mybatis配置文件中指定一些属性
      void setProperties(Properties properties);
     
    }
    

    plugin方法中我们可以决定是否要进行拦截。
    intercept方法就是要进行拦截的时候要执行的方法。

    Mybatis中SqlSession下的四大核心组件:ParameterHandler 、ResultSetHandler 、StatementHandler 、Executor 。Mapper执行的过程也是这四个组件来完成的。他们包含的方法如下:
    Executor
    (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
    ParameterHandler
    (getParameterObject, setParameters)
    StatementHandler
    (prepare, parameterize, batch, update, query)
    ResultSetHandler
    (handleResultSets, handleOutputParameters)

    plugin方法参数可以接收到 这四个核心组件,通常拦截StatementHandler 、Executor。
    拦截 return Plugin.wrap(target, this);
    不拦截 return target;

    intercept方法
    最后要加return invocation.proceed();
    继续执行

    实现接口的类的重要注解

    @Intercepts用于表明当前的对象是一个Interceptor,
    而@Signature则表明要拦截的接口、方法以及对应的参数类型。

    @Intercepts( {       
    @Signature(method = "query", type = Executor.class, args = { MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class }),      
    @Signature(method = "prepare", type = StatementHandler.class, args = { Connection.class }) })
    

    第一个@Signature我们定义了该Interceptor将拦截Executor接口中参数类型为MappedStatement、Object、RowBounds和ResultHandler的query方法;
    第二个@Signature我们定义了该Interceptor将拦截StatementHandler中参数类型为Connection的prepare方法。

    注册拦截器

    mybatis配置文件中

    <configuration>
        <plugins>
           <plugin interceptor="com.tiantian.mybatis.interceptor.MyInterceptor">
               <property name="prop1" value="prop1"/>
           </plugin>
        </plugins>
    

    拦截原理

    mybatis执行sql过程 产生sql语句->产生statement->执行sql语句

    在产生statement过程中可以拦截。
    由于Statement语句是通过RoutingStatementHandler对象的prepare方法生成的。所以,拦截StatementHandler接口的prepare方法就可以更改sql语句。因为包括sql等其他属性在内的多个属性对外部都是封闭的,是对象的私有属性,所以要引入反射机制来获取或者更改对象的私有属性。

    sqlsession四大接口对象介绍

    Executor(接口)
    它是一个执行器,真正进行java与数据库交互的对象,实际干活的。
    StatementHandler(接口)
    它是语句处理器,处理数据库会话的。
    ParameterHandler:
    它是对预编译语句进行参数的设置,完成对预编译参数的设置。
    ResultSetHandler
    返回结果,改变率很低。

    四大对象的调用关系

    Executor先调用StatementHandler里prepa方法预编译SQL语句,并设置参数,然后再用parameterize方法来使用ParameterHandler设置参数,完成预编译,执行查询的话,使用ResultHandler将结果返回给调用者,其他操作也类似。

    简单打印的demo

    import java.sql.Connection;
    import java.util.Properties;
     
    import org.apache.ibatis.executor.Executor;
    import org.apache.ibatis.executor.statement.StatementHandler;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.plugin.Intercepts;
    import org.apache.ibatis.plugin.Invocation;
    import org.apache.ibatis.plugin.Plugin;
    import org.apache.ibatis.plugin.Signature;
    import org.apache.ibatis.session.ResultHandler;
    import org.apache.ibatis.session.RowBounds;
     
    @Intercepts( {
           @Signature(method = "query", type = Executor.class, args = {
                  MappedStatement.class, Object.class, RowBounds.class,
                  ResultHandler.class }),
           @Signature(method = "prepare", type = StatementHandler.class, args = { Connection.class }) })
    public class MyInterceptor implements Interceptor {
     
        public Object intercept(Invocation invocation) throws Throwable {
           Object result = invocation.proceed();
           System.out.println("Invocation.proceed()");
           return result;
        }
     
        public Object plugin(Object target) {
           return Plugin.wrap(target, this);
        }
     
        public void setProperties(Properties properties) {
           String prop1 = properties.getProperty("prop1");
           String prop2 = properties.getProperty("prop2");
           System.out.println(prop1 + "------" + prop2);
        }
     
    }
    
    //来源:https://blog.csdn.net/moshenglv/article/details/52699976
    

    mybatis配置文件中注册后
    即可在每次运行查询时观察到打印语句

    项目中编写的权限拦截器

    1. mybatis拦截器常用于分页器,网上大多代码也是讲的分页器。我用的框架中已自带分页器,所以不再需要自己编写.
    2. 在mybatis配置文件中注册时发现,原本以为分页器最后执行应该最后注册,实际上却发现越后执行的拦截器就要放在越上面,这个拦截器我放在了最下面。
    3. 网上常说的拦截RoutingStatementHandler ,不知道为何,分页器和此拦截器有一个拦截RoutingStatementHandler 后,另一个就拦截不到了,因此此拦截器直接拦截的StatementHandler。

    此拦截器根据查询参数的Map中是否包含 permission参数 来决定是否拦截select函数,从而进行页面展示的拦截。

    import java.lang.reflect.Field;
    import java.sql.Connection;
    import java.util.Map;
    import java.util.Properties;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpSession;
    import org.apache.ibatis.executor.Executor;
    import org.apache.ibatis.executor.statement.RoutingStatementHandler;
    import org.apache.ibatis.executor.statement.StatementHandler;
    import org.apache.ibatis.mapping.BoundSql;
    import org.apache.ibatis.mapping.MappedStatement;
    import org.apache.ibatis.plugin.Interceptor;
    import org.apache.ibatis.plugin.Intercepts;
    import org.apache.ibatis.plugin.Invocation;
    import org.apache.ibatis.plugin.Plugin;
    import org.apache.ibatis.plugin.Signature;
    import org.apache.ibatis.reflection.MetaObject;
    import org.apache.ibatis.reflection.SystemMetaObject;
    import org.apache.ibatis.session.ResultHandler;
    import org.apache.ibatis.session.RowBounds;
    import org.loushang.bsp.security.author.intercept.web.FilterInvocation;
    import org.loushang.bsp.security.session.ISessionStore;
    import org.loushang.bsp.security.session.SessionStoreFactory;
    import org.loushang.bsp.util.StringUtil;
    import org.springframework.beans.factory.annotation.Autowired;
    
    @Intercepts({@org.apache.ibatis.plugin.Signature(type=org.apache.ibatis.executor.statement.StatementHandler.class, method="prepare", args={Connection.class})})
    public class WebSqlInterceptor implements Interceptor {
     
        
        public Object intercept(Invocation invocation) throws Throwable {
            
             if(invocation.getTarget() instanceof StatementHandler) {
                 StatementHandler delegate = (StatementHandler)invocation.getTarget();
                  BoundSql boundSql = delegate.getBoundSql();
                  Object obj = boundSql.getParameterObject();
    //              if (obj instanceof Permission<?>) {
    //                Permission<?> per = (Permission<?>) obj;
    ////                  
    ////                  MetaObject metaStatementHandler = SystemMetaObject.forObject(delegate);
    ////                  // 分离代理对象链(由于目标类可能被多个插件拦截,从而形成多次代理,通过下面的两次循环
    ////                  // 可以分离出最原始的的目标类)
    ////                  while (metaStatementHandler.hasGetter("h")) {
    ////                      Object object = metaStatementHandler.getValue("h");
    ////                      metaStatementHandler = SystemMetaObject.forObject(object);
    ////                  }
    ////                  // 分离最后一个代理对象的目标类
    ////                  while (metaStatementHandler.hasGetter("target")) {
    ////                      Object object = metaStatementHandler.getValue("target");
    ////                      metaStatementHandler = SystemMetaObject.forObject(object);
    ////                  }
    ////                  MappedStatement mappedStatement = (MappedStatement) metaStatementHandler.getValue("delegate.mappedStatement");
    ////                  boundSql = (BoundSql) metaStatementHandler.getValue("delegate.boundSql");
    //                  
    //                MappedStatement mappedStatement = (MappedStatement) ReflectUtil.getFieldValue(delegate, "mappedStatement");
    //                  //拦截到的prepare方法参数是一个Connection对象
    //                  Connection connection = (Connection)invocation.getArgs()[0];
    //                  //获取当前要执行的Sql语句,也就是我们直接在Mapper映射语句中写的Sql语句
    //                  String sql = boundSql.getSql();
    //                  System.out.println("成功拦截Permission sql:"+sql);
    //                  String filterWebSql=null;
    //                  if("web"==per.getPermissionType()) {
    //                    filterWebSql =permissionGetWebSql(sql,per) ;
    //                  }else if("task"==per.getPermissionType()){
    //                    filterWebSql =permissionGetTaskSql(sql,per) ;
    //                  }else {
    //                    filterWebSql = sql;
    //                  }
    //                  //利用反射设置当前BoundSql对应的sql属性为我们建立好的分页Sql语句
    ////                  ReflectUtil.setFieldValue(boundSql, "sql", filterWebSql);
    //              }else {
                      
                      MappedStatement mappedStatement = (MappedStatement) ReflectUtil.getFieldValue(delegate, "mappedStatement");
                      //拦截到的prepare方法参数是一个Connection对象
                      String sql = boundSql.getSql();
                          Connection connection = (Connection)invocation.getArgs()[0];
    //                    System.out.println("成功拦截select sql:"+sql); 
                          Object parameterObject = boundSql.getParameterObject();
                          if (((parameterObject instanceof Map)) && 
                            (((Map)parameterObject).containsKey("permission"))) {
                              Map paraMap = (Map)parameterObject;
    //                        System.out.println("成功拦截参数Map包含permission的 且value:"+paraMap.get("permission"));
                              if ((paraMap.get("permission") instanceof String)) {
                                  String filterWebSql=sql;
                                  String userId = (String) paraMap.get("userId");
                                  StringBuffer webcondition = new StringBuffer().append("WEBSITE_ID in (select WEBSITE_ID from pub_user_webrole WHERE USER_ID=\"").append(userId).append("\" and ROLE_TYPE=\"1\")");
                                  StringBuffer taskcondition = new StringBuffer().append("tab.TASK_ID in (select TASK_ID from pub_user_taskrole WHERE USER_ID=\"").append(userId).append("\" and ROLE_TYPE=\"1\")");
                                  if("web"==paraMap.get("permission")&&(!"SUPERADMIN".equals(userId))) {
                                      System.out.println("拦截器过滤permission为web的sql");
                                      StringBuffer newsql = new StringBuffer();
                                      newsql.append("select tab.* from(").append(sql).append(") tab where ").append(webcondition);
                                      filterWebSql = new String(newsql);
                                  }
                                  else if("task"==paraMap.get("permission")&&(!"SUPERADMIN".equals(userId))){
                                      System.out.println("拦截器过滤permission为task的sql");
                                      StringBuffer newsql = new StringBuffer();
                                      /**
                                       * 若存在只有taskId没有wensiteId的查询 之后要编写根据taskId查询websiteId的语句。
                                       */
                                      //下面的是任务权限与网站权限混合
                                      //newsql.append("select tab.* from(").append(sql).append(") tab left join crawler_task tab on tab1.TASK_ID = tab2.TASK_ID where ").append(webcondition).append(" and ").append(taskcondition);
                                      newsql.append("select tab.* from(").append(sql).append(") tab where ").append(taskcondition);
                                      filterWebSql = new String(newsql);
                                  }
                                  //利用反射设置当前BoundSql对应的sql属性为我们建立好的分页Sql语句
                                  ReflectUtil.setFieldValue(boundSql, "sql", filterWebSql);
                                }
                          }
                  }
           return invocation.proceed();
        }
     
        //由于分页拦截器只分页传入为Map类型的 传入对象会使分页器失效 所以放弃使用Permission 以下两个函数均未用到
        private String permissionGetWebSql(String sql, Permission<?> per) {
            // TODO Auto-generated method stub
            String userId=per.getUserId();
            StringBuffer newsql = new StringBuffer();
            newsql.append("select * from(").append(sql).append(") tab where WEBSITE_ID in (select WEBSITE_ID from pub_user_webrole WHERE USER_ID=\"").append(userId).append("\"and ROLE_TYPE=\"1\")");
            return new String(newsql);
        }
    
        private String permissionGetTaskSql(String sql, Permission<?> per) {
            String userId=per.getUserId();
            StringBuffer newsql = new StringBuffer();
    //      newsql.append("select * from(").append(sql).append(") tab where WEBSITE_ID in (select WEBSITE_ID from pub_user_webrole WHERE USER_ID=\"").append(userId).append("\"and ROLE_TYPE=\"1\")");
            return sql;
        }
    
        public Object plugin(Object target) {
            if(target instanceof StatementHandler) {
                return Plugin.wrap(target, this);
            }else {
                return target;
            }
        }
     
        public void setProperties(Properties properties) {
           String prop1 = properties.getProperty("prop1");
           String prop2 = properties.getProperty("prop2");
           System.out.println(prop1 + "------" + prop2);
        }
        
        
        /**
         * 利用反射进行操作的一个工具类
         *
         */
        private static class ReflectUtil {
           /**
            * 利用反射获取指定对象的指定属性
            * @param obj 目标对象
            * @param fieldName 目标属性
            * @return 目标属性的值
            */
           public static Object getFieldValue(Object obj, String fieldName) {
               Object result = null;
               Field field = ReflectUtil.getField(obj, fieldName);
               if (field != null) {
                  field.setAccessible(true);
                  try {
                      result = field.get(obj);
                  } catch (IllegalArgumentException e) {
                      // TODO Auto-generated catch block
                      e.printStackTrace();
                  } catch (IllegalAccessException e) {
                      // TODO Auto-generated catch block
                      e.printStackTrace();
                  }
               }
               return result;
           }
          
           /**
            * 利用反射获取指定对象里面的指定属性
            * @param obj 目标对象
            * @param fieldName 目标属性
            * @return 目标字段
            */
           private static Field getField(Object obj, String fieldName) {
               Field field = null;
              for (Class<?> clazz=obj.getClass(); clazz != Object.class; clazz=clazz.getSuperclass()) {
                  try {
                      field = clazz.getDeclaredField(fieldName);
                      break;
                  } catch (NoSuchFieldException e) {
                      //这里不用做处理,子类没有该字段可能对应的父类有,都没有就返回null。
                  }
               }
               return field;
           }
     
           /**
            * 利用反射设置指定对象的指定属性为指定的值
            * @param obj 目标对象
            * @param fieldName 目标属性
             * @param fieldValue 目标值
            */
           public static void setFieldValue(Object obj, String fieldName,
                  String fieldValue) {
               Field field = ReflectUtil.getField(obj, fieldName);
               if (field != null) {
                  try {
                      field.setAccessible(true);
                      field.set(obj, fieldValue);
                  } catch (IllegalArgumentException e) {
                      // TODO Auto-generated catch block
                      e.printStackTrace();
                  } catch (IllegalAccessException e) {
                      // TODO Auto-generated catch block
                      e.printStackTrace();
                  }
               }
            }
        }
     
    }
    

    相关文章

      网友评论

          本文标题:mybatis拦截器及项目应用

          本文链接:https://www.haomeiwen.com/subject/yqtpaftx.html