上一篇解析Jeesuite框架中mybatis拦截器的使用讲到拦截器添加到了sqlSessionFactory的配置类sqlSessionFactory.getConfiguration().addInterceptor(interceptor)
那么拦截器是何时执行以及如何生效的呢?
这个实际上是添加到配置类Configuration的interceptorChain属性了
interceptorChain.addInterceptor(interceptor);
我们先来看这个拦截器接口
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
Object plugin(Object target);
void setProperties(Properties properties);
}
关键的是这个intercept()方法,就是执行拦截逻辑的.它的的实现类里做了三件事情.
1.循环执行拦截器的处理类,调用拦截器onInterceptor()
2.执行原有方法逻辑
3.执行拦截器处理类的结束方法
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object result = null;
boolean proceed = false;
for (InterceptorHandler handler : interceptorHandlers) {
result = handler.onInterceptor(invocation);
if(result != null)break;
}
if(result == null){
result = invocation.proceed();
proceed = true;
}
for (InterceptorHandler handler : interceptorHandlers) {
handler.onFinished(invocation,proceed ? result : null);
}
return result;
}
我们在看看它在哪被调用的它在Plugin的invoke()方法中被调用
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
Set<Method> methods = signatureMap.get(method.getDeclaringClass());
if (methods != null && methods.contains(method)) {
return interceptor.intercept(new Invocation(target, method, args));
}
return method.invoke(target, args);
} catch (Exception e) {
throw ExceptionUtil.unwrapThrowable(e);
}
}
这个Plugin实现了 InvocationHandler.
当执行一个sql的时候,例如
thirdpayChannelacctMapper.selectByPrimaryKey(channelId)
debug的时候thirdpayChannelacctMapper就是MapperProxy代理类了
此时是由MapperProxy类来代理的,调用org.apache.ibatis.binding.MapperProxy#invoke方法来处理.我们看其构造方法的参数,sqlSession,mapper接口,以及方法.
MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache)
然后调用了org.apache.ibatis.binding.MapperMethod#execute(),在这里看到了我们熟悉的sqlSession.selectOne()方法
result = sqlSession.selectOne(command.getName(), param);
然后往下看,并不是直接使用,还是用代理sqlSessionProxy处理,
this.sqlSessionProxy.<T> selectOne(statement, parameter);
然后执行拦截器SqlSessionInterceptor方法
org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke
然后执行方法,返回结果.
Object result = method.invoke(sqlSession, args);
最后是Executor执行的结果.Executor分BaseExecutor和CachingExecutor.
像简单sql就是通过BaseExecutor的子类SimpleExecutor来执行,这里调用了prepareStatement()方法,获取了Statement用于后续执行sql.
stmt = prepareStatement(handler, ms.getStatementLog());
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
stmt = prepareStatement(handler, ms.getStatementLog());
return handler.<E>query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
然后BaseStatementHandler的子类PreparedStatementHandler处理
@Override public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException { PreparedStatement ps = (PreparedStatement) statement; ps.execute(); return resultSetHandler.<E> handleResultSets(ps); }
早前,我们使用jdbc也是使用PreparedStatement来执行sql的
Connection connection = DriverManager.getConnection(url, username, password);
String sql = "update user set username='admin' where id = 1";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
int result = preparedStatement.executeUpdate();
上面提到了prepareStatement()方法,也是获取connection,初始化statement.
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
Connection connection = getConnection(statementLog);
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
handler.parameterize(stmt)是用于设置sql参数,替换掉参数的占位符.DefaultParameterHandler是ParameterHandler接口的默认实现.
查询到结果后就是结果的处理
resultSetHandler.<E> handleResultSets(ps);
需要用到另一种handler是ResultSetHandler,只有一个默认实现DefaultResultSetHandler
实现ResultSetHandler这个接口也可以自定义结果处理类.至此,流程结束.
总结一下:
MapperProxy#invoke
MapperMethod#execute()
sqlSession.<E>selectList()
SqlSessionTemplate#selectList(java.lang.String, java.lang.Object)
this.sqlSessionProxy.<E> selectList(statement, parameter)
DefaultSqlSession#selectList(java.lang.String, java.lang.Object)
CachingExecutor#query()开启缓存就会先调这个,如果缓存为空就调SimpleExecutor
SimpleExecutor#doQuery
RoutingStatementHandler#query
PreparedStatementHandler#query
PreparedStatement.execute()
RoutingStatementHandler这里做了个路由
switch (ms.getStatementType()) {
case STATEMENT:
delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case PREPARED:
delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
case CALLABLE:
delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
break;
default:
throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
}
那怎么知道是什么类型呢?DefaultSqlSession#selectList(java.lang.String, java.lang.Object, org.apache.ibatis.session.RowBounds) 这个方法里自动创建了
MappedStatement ms = configuration.getMappedStatement(statement);