一、mybatis总体执行流程
如图所示:
以上为mybatis大概的执行流程,细节部分在下面详细阐述。
二、Mybatis-config 配置文件解析
1. 配置文件说明
配置文件为:mybatis-config.xml,配置文件中主要包含了对11个元素节点,具体如下:
节点 | 说明 |
---|---|
properties | 属性及属性文件配置可以通过properties配置文件来动态获取数据 |
settings | 一些布尔参数值的设置 |
typeAliases | 通过对po对象位置所在建立别名,避免mapper.xml中繁琐的去指定完全限定名 |
plugins | 配置自定义插件,使自定义插件生效 |
objectFactory | 指定自定义对象工厂来覆盖mybatis默认对象工厂来创建结果集的对象 |
objectWrapperFactory | |
reflectorFactory | |
environments | 配置jdbc,数据源等环境,一个环境对应一个SqlSessionFactory实例 |
databaseIdProvider | 数据库厂商标识 |
typeHandlers | java类型与jdbc类型的对应关系,提供了默认的对应关系,一般无须指定 |
mappers | 配置具体的mapper.xml文件所在位置,有四种配置方式, package优先 |
2.配置文件解析流程
配置文件的解析主要由XmlConfigBuilder 的parseConfiguration方法进行解析如下:
private void parseConfiguration(XNode root) {
try {
// 按11个配置节点依次解析
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
在实例化XmlConfigBuilder对象的时候会将通过Resuore获取的配置文件的流做为参数传入,并实例一个XpathParser对象来对xml进行解析。
paserConfiguration方法中每一个方法的解析过程的结果都会去更新Configuration对象的状态。
最终通过parse方法返回this.configuration。
2.1 propertiesElement方法
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
// 获取所有子节点属性,<property></property>, Properties 继承了hashTable 本质是一个特定的集合
Properties defaults = context.getChildrenAsProperties();
// 获取资源属性的值
String resource = context.getStringAttribute("resource");
// 获取url属性的值
String url = context.getStringAttribute("url");
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
// 获取configuration实例中properties的值
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
// 将解析的属性key-value重新设置到parser和configuration中
parser.setVariables(defaults);
configuration.setVariables(defaults);
}
}
2.2 settingsAsProperties 方法
// settingsAsProperties 方法主要为解析settings标签并检查解析的key-value是否与configuration中规定的吻合
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
Properties props = context.getChildrenAsProperties();
// 检测解析的属性是否为Configuration对象中规定的,不存在抛异常
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
for (Object key : props.keySet()) {
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
2.3 typeAliasesElement 方法
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
// <package name=""/> 标签
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
// <typeAlias alias="role" type="com.mamba.po.Role" /> 标签
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
Class<?> clazz = Resources.classForName(type);
// typeAliasRegistry是在Configuration中new出来的, typeAliasRegistry中包含一个typeAlias的集合
if (alias == null) {
// 如果alias没有配置,会采用Class.getSimpleName() 获得一个,例如,Role -> role
typeAliasRegistry.registerAlias(clazz);
} else {
// 如果typeAlias包含当前key,并且对应的值不为空且当前值与集合中的值不一致,说明key重复不唯一,会抛出异常
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
2.4 pluginElement 方法
配置文件中标签格式如下:
<plugins>
<plugin interceptor="">
<property name="" value=""/>
</plugin>
</plugins>
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
// 遍历 plugin
for (XNode child : parent.getChildren()) {
// 获取interceptor属性值
String interceptor = child.getStringAttribute("interceptor");
// 获取所有子节点属性值
Properties properties = child.getChildrenAsProperties();
// 实例化Interceptor
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
// 将对象属性赋值
interceptorInstance.setProperties(properties);
// 加入到Configuration对象的拦截器链中
configuration.addInterceptor(interceptorInstance);
}
}
}
2.5 environmentsElement方法
environment主要是对事物管理器与数据源的配置管理。
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
// 获取指定的默认数据源 <environments default="xxx" />
environment = context.getStringAttribute("default");
}
// 可能存在多数据源配置
for (XNode child : context.getChildren()) {
// environment指定的的id属性
String id = child.getStringAttribute("id");
// 判断当前id指示的environment是不是默认的
if (isSpecifiedEnvironment(id)) {
// 事物管理器工厂对象 TransactionFactory 只是接口,具体实现类由<transactionManager type="">中type指定的决定
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// 数据源工厂对象 同事物管理器一样,实现类由 datasource中 type 决定
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
// 通过Environment中静态内部类Builder来构建Environment
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
// build()方法内部也是new Environment(id, tx, datasource)
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
2.6 mapperElement 方法
该方法是解析mpper.xml文件的核心方法,后面详细分析,每种配置对应的具体解析方式
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
// 可能从在多个<mapper> 所以采用循环
for (XNode child : parent.getChildren()) {
// 如果是<package> 方式
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
// 解析每一个<mapper>标签,属性可以是 resource url class 三种之一
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
// resource方式 resource="com.mamba.mapper.XxxMapper"
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
// 解析 mapper.xml文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
// url 方式 url="file:///var/mappers/AuthorMapper.xml"
ErrorContext.instance().resource(url);
InputStream inputStream = Resources.getUrlAsStream(url);
// 解析 mapper.xml 文件
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url == null && mapperClass != null) {
// class 方式 通过指定接口找mapper.xml文件进行解析
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
三、Mapper.xml 文件解析
1. 元素节点说明
节点 | 说明 |
---|---|
cache | namespace下的二级缓存 |
cache-ref | 在另一个namespace下缓存也有效 |
resultMap | 结果集封装 |
sql | sql片段 |
insert | 增加语句 |
update | 修改语句 |
delete | 删除语句 |
select | 查询语句 |
2. 解析方式
2.1 Package配置方式
在XmlConfigBuilder的mapperElement方法中首先获取到package标签的值,具体的解析过程采用MapperRegistry的addMappers方法。
// MapperRegistry 类下的方法:
// superType在解析时被指定为Object.class; packageName = "com.mamba.mapper"
public void addMappers(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
// 该方法会找到mapper包下所有的mapper接口的类对象方式一个matches的set集合中
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
// 对mapper包下获取的mapper接口遍历处理
for (Class<?> mapperClass : mapperSet) {
addMapper(mapperClass);
}
}
// ResoulverUtil 下的find方法:
// test = "is assignable to Object" test是ResoulverUtils中的IsA对象,parent=Object.class
public ResolverUtil<T> find(Test test, String packageName) {
// 获取packgeName路径,将.替换成/ ; path = "com/mamba/mapper"
String path = getPackagePath(packageName);
try {
// 找出mapper包下所有文件名,包括.class与.xml
List<String> children = VFS.getInstance().list(path);
for (String child : children) {
if (child.endsWith(".class")) {
// 通过.class文件获得对应的Class类对象并添加到matches的set集合
addIfMatching(test, child);
}
}
} catch (IOException ioe) {
log.error("Could not read package: " + packageName, ioe);
}
return this;
}
//VFS实例的list方法
public List<String> list(String path) throws IOException {
List<String> names = new ArrayList<>();
// getResources方法获取绝对路径Url,Url为一个对象,这里为:“/G:/xxx/../com/mamba/mapper”
for (URL url : getResources(path)) {
// 该list方法有子类实现
names.addAll(list(url, path));
}
return names;
}
// 子类DeafultVFS的list方法
public List<String> list(URL url, String path) throws IOException {
InputStream is = null;
try {
List<String> resources = new ArrayList<>();
URL jarUrl = findJarForResource(url);
if (jarUrl != null) {
// ...省略
}
else {
List<String> children = new ArrayList<>();
try {
if (isJar(url)) {
// ...省略
}
else {
is = url.openStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
List<String> lines = new ArrayList<>();
// 遍历读取的文件夹下的所有文件名
for (String line; (line = reader.readLine()) != null;) {
lines.add(line);
if (getResources(path + "/" + line).isEmpty()) {
lines.clear();
break;
}
}
if (!lines.isEmpty()) {
children.addAll(lines);
}
}
} catch (FileNotFoundException e) {
// ...省略
}
String prefix = url.toExternalForm();
if (!prefix.endsWith("/")) {
prefix = prefix + "/";
}
// 遍历递归,如果该文件夹下还有子文件夹
for (String child : children) {
String resourcePath = path + "/" + child;
resources.add(resourcePath);
URL childUrl = new URL(prefix + child);
resources.addAll(list(childUrl, resourcePath));
}
}
return resources;
} finally {
//省略....
}
}
// ResoulverUtil 下的addIfMatching方法:
protected void addIfMatching(Test test, String fqn) {
try {
// 去掉.class后缀并替换'/'为'.' 即变成这样: com.mamba.mapper.RolerMapper
String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
ClassLoader loader = getClassLoader();
Class<?> type = loader.loadClass(externalName);
// mataches方法主要调用Class类的isAssignableFrom(type)的方法判断type是否为调用者的子类或子接口
if (test.matches(type)) {
// 将当前类对象加入matches集合
matches.add((Class<T>) type);
}
} catch (Throwable t) {
}
}
MapperRegistry对象的addMapper方法
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
// 判断当前KnownMappers集合中是否已经加载该接口的代理对象
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 实例化MapperPorxyFactory对象并加入集合,通过MapperPorxyFactory可以获取type接口的代理对象
knownMappers.put(type, new MapperProxyFactory<>(type));
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
// 解析type对应的mapper.xml文件
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
// MapperAnnotationBuilder的parse方法
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
// 解析mapper.xml的方法
loadXmlResource();
// 解析之后会加入到Configuration的LoadedResource集合中,避免重复加载
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
parseCache();
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
// 解析
private void loadXmlResource() {
if (!configuration.isResourceLoaded("namespace:" + type.getName())) {
// 通过type的类对象获取对应的.xml文件,并将.xml文件变成流
String xmlResource = type.getName().replace('.', '/') + ".xml";
InputStream inputStream = type.getResourceAsStream("/" + xmlResource);
if (inputStream == null) {
try {
inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);
} catch (IOException e2) {
}
}
if (inputStream != null) {
// 通过XMLMapperBuilder 的parse来完成对各节点数据的映射封装
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());
xmlParser.parse();
}
}
}
2.2 resource与url方式
resource与Url无需通过MapperRegistry的addMapper方法,而是直接将指定的xml读取成流,然后通过new XMLMapperBuilder对象调用parse方法进行解析
2.3 class方式
class配置方式的解析与package方式大致一样,class方式省略了遍历包,会直接通过MapperRegistry对象的addMapper方法来解析。
2.4 四种方式梳理的流程图
3. XMLMapperBuilder对象对mapper.xml的真正解析
3.1 parse方法
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
// 对各个元素节点的数据读取
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
// 通过nameSpace创建MapperProxyFactory对象
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
3.2 configurationElement() 方法
private void configurationElement(XNode context) {
try {
// 或取namespace值并设置到builderAssistant中
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
// cache-ref标签解析
cacheRefElement(context.evalNode("cache-ref"));
// cache 标签解析
cacheElement(context.evalNode("cache"));
// parameterMap 标签解析
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// resultMap标签解析
resultMapElements(context.evalNodes("/mapper/resultMap"));
// sql标签解析
sqlElement(context.evalNodes("/mapper/sql"));
// select insert update delete标签解析
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
3.2.1 cache-ref标签
<cache-ref namespace="com.someone.application.data.SomeMapper"/>
没有子节点,属性主要为namespace; 由cacheReflElement()方法解析
private void cacheRefElement(XNode context) {
if (context != null) {
// 将引用namespace加入configuration的CacheRef集合中,建立当前namespace与引用namespace的关系
configuration.addCacheRef(builderAssistant.getCurrentNamespace(), context.getStringAttribute("namespace"));
CacheRefResolver cacheRefResolver = new CacheRefResolver(builderAssistant, context.getStringAttribute("namespace"));
try {
// 实际调用MapperBuilderAssistant的useCacheRef(),该方法可能会抛异常,即在Configuration中还不存在当前引用namespace的cache
cacheRefResolver.resolveCacheRef();
} catch (IncompleteElementException e) {
// 在上述抛异常的情况下,即未完成的情况下,会将cacheRefResolver加入到Configuration中的未完成的引用namespace的缓存集合中--
configuration.addIncompleteCacheRef(cacheRefResolver);
}
}
}
如图所示: cache 与 cacheRef 引用生成的关系
3.2.2 cache 标签
private void cacheElement(XNode context) {
if (context != null) {
// 可以指定type, 默认perpertual
String type = context.getStringAttribute("type", "PERPETUAL");
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
// 回收策略,默认LRU, 有FIFO, SOFT, WEAK四种
// LRU移除最长时间不用的,FIFO按添加的顺序移除,SOFT基于垃圾收集器与软引用对象,WEAK基于垃圾收集器与弱应用规则对象
String eviction = context.getStringAttribute("eviction", "LRU");
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
// 刷新策略
Long flushInterval = context.getLongAttribute("flushInterval");
// 引用数目,默认1024
Integer size = context.getIntAttribute("size");
// 默认false, 需要实体映射对象实现序列化,否则会抛异常
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
boolean blocking = context.getBooleanAttribute("blocking", false);
Properties props = context.getChildrenAsProperties();
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
3.2.3 resultMap 标签
resultMap的子节点标签是比较多的,属性有:{id, type, autoMapping,extends, ofType, resultType, javaType}, {type,ofType, resultType, javaType}是等价的,子元素节点有:
- constructor{ idArg(column, javaType), arg }
- id(property, column)
- result(property, column)
- association(property, javaType)
- collection(property, ofType)
- discriminator{case(value, resultType),(column, javaType)}
解析的大致流程如下:
resultMap节点的解析后对应的java对象为ResultMap;
public class ResultMap {
private Configuration configuration;
private String id;
private Class<?> type;
private List<ResultMapping> resultMappings;
private List<ResultMapping> idResultMappings;
private List<ResultMapping> constructorResultMappings;
private List<ResultMapping> propertyResultMappings;
private Set<String> mappedColumns;
private Set<String> mappedProperties;
private Discriminator discriminator;
private boolean hasNestedResultMaps;
private boolean hasNestedQueries;
private Boolean autoMapping;
// 省略
}
3.2.4 sql 标签
sql标签解析的就保存在XmlMapperBuilder 的sqlFragments 集合中,没有给对应的Java对象。
3.2.5 select/insert/update/delete标签
这些标签解析后对应的java对象为MappedStatement;
public final class MappedStatement {
private String resource;
private Configuration configuration;
private String id;
private Integer fetchSize;
private Integer timeout;
private StatementType statementType;
private ResultSetType resultSetType;
private SqlSource sqlSource;
private Cache cache;
private ParameterMap parameterMap;
private List<ResultMap> resultMaps;
private boolean flushCacheRequired;
private boolean useCache;
private boolean resultOrdered;
private SqlCommandType sqlCommandType;
private KeyGenerator keyGenerator;
private String[] keyProperties;
private String[] keyColumns;
private boolean hasNestedResultMaps;
private String databaseId;
private Log statementLog;
private LanguageDriver lang;
private String[] resultSets;
// 省略
}
由于代码太多,具体解析过程就不贴了。
3.3 各标签解析对应的java对象以及Configuration中的存储
Configuration对象中的关于存储解析的Mapper数据存储的代码片段:
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection")
.conflictMessageProducer((savedValue, targetValue) ->
". please check " + savedValue.getResource() + " and " + targetValue.getResource());
protected final Map<String, Cache> caches = new StrictMap<>("Caches collection");
protected final Map<String, ResultMap> resultMaps = new StrictMap<>("Result Maps collection");
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<>("Parameter Maps collection");
protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<>("Key Generators collection");
protected final Set<String> loadedResources = new HashSet<>();
protected final Map<String, XNode> sqlFragments = new StrictMap<>("XML fragments parsed from previous mappers");
到此,xml配置文件的解析及Configuration对象的实例化装配就完成了。
4. SqlSession的构建
SqlSession通过SqlSessionFactory的openSession()方法获得,openSession()是一个重载方法应对多种情况。以DefaultSqlSessionFactory为例,具体执行方法为openSessionFromDataSource和openSessionFromConnection两个。
private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
Transaction tx = null;
try {
final Environment environment = configuration.getEnvironment();
final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
// execType默认为Simple
final Executor executor = configuration.newExecutor(tx, execType);
return new DefaultSqlSession(configuration, executor, autoCommit);
} catch (Exception e) {
closeTransaction(tx); // may have fetched a connection so lets call close()
throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
5. Executor
Executor在ExecutorType枚举类中定义了三种,为: SIMPLE, REUSE, BATCH,默认会采用SIMPLE。Executor的继承关系图如下:
三种实现类的方法,如下:
Configuration中实例化Executor的源码:
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
// defaultExecutorType = ExecutorType.SIMPLE
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
// 如果启用了缓存,将executor再封装,CachingExecutor采用了装饰者模式
executor = new CachingExecutor(executor);
}
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}
6. StatementHandler
StatementHandler接口的具体实现类在StatementType中定义了三种,为:STATEMENT, PREPARED, CALLABLE,如图所示:
三种实现类的方法, 如图:
而RoutingStatementHandler类是这三种的装饰类,这是采用了装饰者的设计模式。源码片段:
// 构造方法
public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
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());
}
}
Configuration对象中实例化StatementHander的源码片段:
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
StatementType默认为StatementType.PREPARED,具体实现在MappedStatement类Builder构造方法中指定的。
7. ResultSetHandler与 ResultHandler处理查询结果
ResultHandler处理结果集采用了工厂方法,这个结果集的工厂方法可以自己定义,即在mybatis-config.xml中objectFactory标签里指定自定义的工厂类,mybatis默认采用DefaultObjectFactory。在Configuration中可以找到该默认的工厂类。
ResultSetHandler接口的主要实现类为DefualtResultSetHandler。该实例初始化的具体方法也是在Configuration中,源码如下:
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
StatementHandler中持有ResultSetHandler属性,所有在StatementHandler实例化的时候就会调用该方法去实例化ResultSetHandler做为该属性的值。
处理结果集的大致流程如下,省略了许多细节:
storeObject之前还有一个getRowValue方法,该方法中调用了createResultObject()方法,该方法的具体实现里就用到了Configuration中的创建代理对象方法来创建一个Mapper接口的代理对象。storeObject()方法首先将该代理对象设置到ResultContext中,然后调用resultHander的handleResult()方法,将resultObject添加到ResultHandler的list集合中。
到此,执行流程的全过程就结束了!