由于本人才疏学浅,刚刚入门。本文章是我在实现数据权限的过程中的学习体会。
总体思想
一、Mybatis拦截器
参考:
Mybatis中文官网
慕课网Mybatis方面视频
SQL解析
引用官网说明:
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
Executor(update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler(getParameterObject, setParameters)
ResultSetHandler(handleResultSets, handleOutputParameters)
StatementHandler(prepare, parameterize, batch, update, query)
通过 MyBatis 提供的强大机制,使用插件是非常简单的,只需实现 Interceptor 接口,并指定了想要拦截的方法签名即可。
Mybatis所提供的功能是Plugin,虽然应译为插件,但是实质就是指的我们所需要使用的拦截器。
方法及参数解析:
1. Interceptor 接口
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
实现 Interceptor 接口也就是实现intercept,plugin,setProperties这三个方法,其中
①intercept方法是我们拦截到对象后所进行操作的位置,也就是我们之后编写逻辑代码的位置。
②plugin方法,根据参数可以看出,该方法的作用是拦截我们需要拦截到的对象。
③setProperties方法,我们可以通过配置文件中进行properties配置,然后在该方法中读取到配置。
这三个方法的执行顺序: setProperties--->plugin--->intercept
2.intercept方法中的Invocation类的属性
private Object target; //所拦截到的目标的代理
private Method method; //所拦截目标的具体方法
private Object[] args; //方法的参数
实现interceptor接口
@Intercepts({ @Signature(method = "prepare", type = StatementHandler.class, args = { Connection.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) {
}
}
解释:
@intercepts声明该类为拦截器,@signature声明拦截对象。
Mybatis获取Statement是在statementHandler中,因为我们需要拦截的对象应该是Statement,在StatementHandler类中有返回值为Statement的Prepare方法,所以,这个类就是我们需要拦截的对象。
method为我们需要拦截的prepare方法,type为所要拦截的接口类,args为prepare方法的参数。
源码解析:
StatementHandler源码:
public interface StatementHandler {
Statement prepare(Connection connection)
throws SQLException;
void parameterize(Statement statement)
throws SQLException;
void batch(Statement statement)
throws SQLException;
int update(Statement statement)
throws SQLException;
<E> List<E> query(Statement statement, ResultHandler resultHandler)
throws SQLException;
BoundSql getBoundSql();
ParameterHandler getParameterHandler();
}
该源码中的prepare方法为我们需要的拦截的,它的实现为:
实际的实现方法在BaseStatementHandler中:
@Override
public Statement prepare(Connection connection) throws SQLException {
ErrorContext.instance().sql(boundSql.getSql());
Statement statement = null;
try {
statement = instantiateStatement(connection);//<-----也就是这个方法
setStatementTimeout(statement);
setFetchSize(statement);
return statement;
} catch (SQLException e) {
closeStatement(statement);
throw e;
} catch (Exception e) {
closeStatement(statement);
throw new ExecutorException("Error preparing statement. Cause: " + e, e);
}
}
protected abstract Statement instantiateStatement(Connection connection) throws SQLException;
该方法为抽象方法,它的实现为
由于我们的是预编译的sql,所以就是PreparedStatementHandler类中的实现方法
@Override
protected Statement instantiateStatement(Connection connection) throws SQLException {
String sql = boundSql.getSql();//<----这就是我们的sql语句
if (mappedStatement.getKeyGenerator() instanceof Jdbc3KeyGenerator) {
String[] keyColumnNames = mappedStatement.getKeyColumns();
if (keyCol