MyBATIS原理第三篇: SqlSession下的四大对象之一——执行器(executor)

来源:互联网 发布:最终幻想mac版 编辑:程序博客网 时间:2024/06/05 03:06

首先我先解释一下标题 四大对象是指:executor, statementHandler,parameterHandler,resultHandler对象。(为了方便下面的文章说道四大对象就专指它们)

它们都是sqlSession的底层类实现,也是插件能够拦截的四大对象。所以这里已经触及了MyBATIS的底层,动态代理,反射随时可以看到,如果没有第一篇作为基础,你将十分难以理解它。了解他们的协作,是插件编写的基础之一,所以这是十分的重要。

Executor在sqlSession中的应用

上篇我们谈到了一个问题,一个mapper被执行是通过动态代理来完成的,然后进入到了sqlSession的方法中去。这个并不难理解,但是sqlSession内部是怎么运行的呢?答案四大对象的协作。在SqlSession它还是一个接口,mybatis内部是通过DefaultSqlSession这个实现类为我们提供服务的,它比较长,但是我们不需要全部看到,我们只看到很常用的selectList方法便可以了。

[java] view plain copy
  1. package org.apache.ibatis.session.defaults;  
  2. public class DefaultSqlSession implements SqlSession {  
  3.   
  4.   private Configuration configuration;  
  5.   private Executor executor;  
  6.   
  7.   private boolean autoCommit;  
  8.   private boolean dirty;  
  9.   
  10. .......  
  11. @Override  
  12.   public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds) {  
  13.     try {  
  14.       MappedStatement ms = configuration.getMappedStatement(statement);  
  15.       return executor.query(ms, wrapCollection(parameter), rowBounds, Executor.NO_RESULT_HANDLER);  
  16.     } catch (Exception e) {  
  17.       throw ExceptionFactory.wrapException("Error querying database.  Cause: " + e, e);  
  18.     } finally {  
  19.       ErrorContext.instance().reset();  
  20.     }  
  21.   }  
  22. ......  
  23. }  

我们可以看到它是通过executor去执行方法来完成查询的。

初认Executor

那么我们对executor就很感兴趣,于是我们看看executor是怎么样的,首先在MyBATIS中有三种executor:

SimpleExecutor -- SIMPLE 就是普通的执行器。

ReuseExecutor -执行器会重用预处理语句(prepared statements)

BatchExecutor --它是批量执行器

这些就是mybatis的三种执行器。你可以通过配置文件的settings里面的元素defaultExecutorType,配置它,默认是采用SimpleExecutor如果你在Spring运用它,那么你可以这么配置它:

[html] view plain copy
  1. <bean id="sqlSessionTemplateBatch" class="org.mybatis.spring.SqlSessionTemplate">       
  2. <constructor-arg index="0" ref="sqlSessionFactory" />    
  3. <!--更新采用批量的executor -->    
  4. <constructor-arg index="1" value="BATCH"/>    
  5. </bean>    
这样,它便是一个批量的执行器。mybatis的三个executor都有一个共同的父类——BaseExecutor。


Executor初始化

首先我们先了解一下mybatis是怎么样生成executor的。我们看到生成Executor的地方(org.apache.ibatis.session.Configuration):

[java] view plain copy
  1. public Executor newExecutor(Transaction transaction, ExecutorType executorType) {  
  2.     executorType = executorType == null ? defaultExecutorType : executorType;  
  3.     executorType = executorType == null ? ExecutorType.SIMPLE : executorType;  
  4.     Executor executor;  
  5.     if (ExecutorType.BATCH == executorType) {  
  6.       executor = new BatchExecutor(this, transaction);  
  7.     } else if (ExecutorType.REUSE == executorType) {  
  8.       executor = new ReuseExecutor(this, transaction);  
  9.     } else {  
  10.       executor = new SimpleExecutor(this, transaction);  
  11.     }  
  12.     if (cacheEnabled) {  
  13.       executor = new CachingExecutor(executor);  
  14.     }  
  15.     executor = (Executor) interceptorChain.pluginAll(executor);  
  16.     return executor;  
  17.   }  

这里大部分都很好理解,但是有个地方就好不好理解,它就是:

[java] view plain copy
  1. executor = (Executor) interceptorChain.pluginAll(executor);  
这是一段非常重要的代码,它是采用责任链模式,来产生代理对象。我们需要再深入理解它,打开它具体的pluginAll方法:

[java] view plain copy
  1. public Object pluginAll(Object target) {  
  2.     for (Interceptor interceptor : interceptors) {  
  3.       target = interceptor.plugin(target);  
  4.     }  
  5.     return target;  
  6.   }  

我们这里先介绍一下这段代码:

Interceptor它是mybatis拦截器必须要实现的接口,换句话说,这个遍历就是遍历mybatis的拦截器。

然后调用plugin方法,这个方法是为了生成代理对象(占位)的。

于是可以想象我们的插件的代理对象将会是一层层的嵌套,所以当中任何一个插件(Interceptor)都有机会拦截这个真是的服务对象(executor),则便是责任链模式,我们完全可以提供插件(Interceptor),进入到代理对象的invoke方法里面,来改变executor的行为和方法。

但是我要在这里强调,当你使用插件的时候,你将改变mybatis的executor内容实现,你必须慎重的使用它。


如果要我们自己编写动态的代理,那么工作量可不小,好在mybatis为我们提供了Plugin.java类来完成我们所需要的功能。

[java] view plain copy
  1. public class Plugin implements InvocationHandler {  
  2.   
  3.   private Object target;  
  4.   private Interceptor interceptor;  
  5.   private Map<Class<?>, Set<Method>> signatureMap;  
  6.   
  7.   private Plugin(Object target, Interceptor interceptor, Map<Class<?>, Set<Method>> signatureMap) {  
  8.     this.target = target;  
  9.     this.interceptor = interceptor;  
  10.     this.signatureMap = signatureMap;  
  11.   }  
  12.   
  13.   public static Object wrap(Object target, Interceptor interceptor) {  
  14.     Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);  
  15.     Class<?> type = target.getClass();  
  16.     Class<?>[] interfaces = getAllInterfaces(type, signatureMap);  
  17.     if (interfaces.length > 0) {  
  18.       return Proxy.newProxyInstance(  
  19.           type.getClassLoader(),  
  20.           interfaces,  
  21.           new Plugin(target, interceptor, signatureMap));  
  22.     }  
  23.     return target;  
  24.   }  
  25.     
  26.   @Override  
  27.   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
  28.     try {  
  29.       Set<Method> methods = signatureMap.get(method.getDeclaringClass());  
  30.       if (methods != null && methods.contains(method)) {  
  31.         return interceptor.intercept(new Invocation(target, method, args));  
  32.       }  
  33.       return method.invoke(target, args);  
  34.     } catch (Exception e) {  
  35.       throw ExceptionUtil.unwrapThrowable(e);  
  36.     }  
  37.   }  
  38.  ......  
  39. }  

这里有一个wrap方法:它会为我们生成代理对象。一旦我们的插件和它绑定,那么我们可以想到就会进入invoke方法里面。

invoke方法:很简单,它运行首先通过class和method的过滤,看看是否需要拦截这个方法,如果被拦截,那么它就运行interceptor的intercept方法。所以当我们配置了签名,就能够拦截我们的方法。

我们先讨论那么多,我们知道后面讲插件的时候我们还会提及它,这里我们知道它会根据插件的个数生成一层层的代理对象就可以了。


executor的执行

executor的执行是依赖于Statement对象来操作的,让我们以SimpleExecutor的doQuery方法为例子:

[java] view plain copy
  1. public class SimpleExecutor extends BaseExecutor {  
  2. ......  
  3.     
  4.   @Override  
  5.   public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {  
  6.     Statement stmt = null;  
  7.     try {  
  8.       Configuration configuration = ms.getConfiguration();  
  9.       StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);  
  10.       stmt = prepareStatement(handler, ms.getStatementLog());  
  11.       return handler.<E>query(stmt, resultHandler);  
  12.     } finally {  
  13.       closeStatement(stmt);  
  14.     }  
  15.   }  
  16.   
  17.   ......  
  18.   private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {  
  19.     Statement stmt;  
  20.     Connection connection = getConnection(statementLog);  
  21.     stmt = handler.prepare(connection);  
  22.     handler.parameterize(stmt);  
  23.     return stmt;  
  24.   }  
  25.   
  26. }  
很显然这里调度的是一个查询方法

首先它先生成StatementHandler对象。

通过prepareStatement方法调用prepare方法初始化参数。

然后使用parameterize方法设置参数到运行环境。

然后便通过handler.<E>query(stmt, resultHandler);方法来完成结果组装。

于是我们的焦点就集中在了StatementHandler对象上,下章我们将谈及它。






http://blog.csdn.net/ykzhen2015/article/details/50315027

阅读全文
0 0
原创粉丝点击