mybatis拦截器及项目应用

目录

拦截器接口
注册拦截器
拦截原理
简单打印的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配置文件中


    
       
           
       
    

拦截原理

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拦截器及项目应用)