目录
拦截器接口
注册拦截器
拦截原理
简单打印的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配置文件中注册后
即可在每次运行查询时观察到打印语句
项目中编写的权限拦截器
- mybatis拦截器常用于分页器,网上大多代码也是讲的分页器。我用的框架中已自带分页器,所以不再需要自己编写.
- 在mybatis配置文件中注册时发现,原本以为分页器最后执行应该最后注册,实际上却发现越后执行的拦截器就要放在越上面,这个拦截器我放在了最下面。
- 网上常说的拦截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();
}
}
}
}
}