Mybatis框架解析之Builder解析
首先我们从builder这个类入手,首先我们注意到BaseBuilder,其实它的本质上市一个抽象类,它从本质上抽象出了Builder的一切,我猜想这里一定使用了建造者模式,但是这个抽象类里面居然没有抽象方法!
其中XXXValueOf方法,其实是把String字符串转换成了相对应的类型,如下代码。
protected Boolean booleanValueOf(String value, Boolean defaultValue) {
return value == null ? defaultValue : Boolean.valueOf(value);
}
protected Integer integerValueOf(String value, Integer defaultValue) {
return value == null ? defaultValue : Integer.valueOf(value);
}
protected Set<String> stringSetValueOf(String value, String defaultValue) {
value = (value == null ? defaultValue : value);
return new HashSet<String>(Arrays.asList(value.split(",")));
}
其中resoveXXXType的目的就是把string转换成相对应的类型。
protected JdbcType resolveJdbcType(String alias) {
if (alias == null) {
return null;
}
try {
return JdbcType.valueOf(alias);
} catch (IllegalArgumentException e) {
throw new BuilderException("Error resolving JdbcType. Cause: " + e, e);
}
}
protected ResultSetType resolveResultSetType(String alias) {
if (alias == null) {
return null;
}
try {
return ResultSetType.valueOf(alias);
} catch (IllegalArgumentException e) {
throw new BuilderException("Error resolving ResultSetType. Cause: " + e, e);
}
}
protected ParameterMode resolveParameterMode(String alias) {
if (alias == null) {
return null;
}
try {
return ParameterMode.valueOf(alias);
} catch (IllegalArgumentException e) {
throw new BuilderException("Error resolving ParameterMode. Cause: " + e, e);
}
}
下面的方法是通过字符串别名解析出相对应的类型,再从类型创建实例。
protected Object createInstance(String alias) {
Class<?> clazz = resolveClass(alias);
if (clazz == null) {
return null;
}
try {
return resolveClass(alias).newInstance();
} catch (Exception e) {
throw new BuilderException("Error creating instance. Cause: " + e, e);
}
}
protected Class<?> resolveClass(String alias) {
if (alias == null) {
return null;
}
try {
return resolveAlias(alias);
} catch (Exception e) {
throw new BuilderException("Error resolving class. Cause: " + e, e);
}
}
注意下面的,是2个不同的重载类,是第一个调用第二个。首先得到相对应的TypeHanlder类型,如果该TypeHanlder在typeHanlderRegisty注册中心有留存,那么返回,否则从javatype里创建一个新的。
protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, String typeHandlerAlias) {
if (typeHandlerAlias == null) {
return null;
}
Class<?> type = resolveClass(typeHandlerAlias);
if (type != null && !TypeHandler.class.isAssignableFrom(type)) {
throw new BuilderException("Type " + type.getName() + " is not a valid TypeHandler because it does not implement TypeHandler interface");
}
@SuppressWarnings( "unchecked" ) // already verified it is a TypeHandler
Class<? extends TypeHandler<?>> typeHandlerType = (Class<? extends TypeHandler<?>>) type;
return resolveTypeHandler(javaType, typeHandlerType);
}
protected TypeHandler<?> resolveTypeHandler(Class<?> javaType, Class<? extends TypeHandler<?>> typeHandlerType) {
if (typeHandlerType == null) {
return null;
}
// javaType ignored for injected handlers see issue #746 for full detail
TypeHandler<?> handler = typeHandlerRegistry.getMappingTypeHandler(typeHandlerType);
if (handler == null) {
// not in registry, create a new one
handler = typeHandlerRegistry.getInstance(javaType, typeHandlerType);
}
return handler;
}
其中MapperBuilderAssistant在此包下面,并且继承了BaseBuilder,下面对此类做一个解析,比如下面的就是解析命名空间的,就是包名。
public String applyCurrentNamespace(String base, boolean isReference) {
if (base == null) {
return null;
}
if (isReference) {
// is it qualified with any namespace yet?
if (base.contains(".")) {
return base;
}
} else {
// is it qualified with this namespace yet?
if (base.startsWith(currentNamespace + ".")) {
return base;
}
if (base.contains(".")) {
throw new BuilderException("Dots are not allowed in element names, please remove it from " + base);
}
}
return currentNamespace + "." + base;
}
下面的代码主要是用namespace得到cache的一个实例,就这么理解。
public Cache useCacheRef(String namespace) {
if (namespace == null) {
throw new BuilderException("cache-ref element requires a namespace attribute.");
}
try {
unresolvedCacheRef = true;
Cache cache = configuration.getCache(namespace);
if (cache == null) {
throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.");
}
currentCache = cache;
unresolvedCacheRef = false;
return cache;
} catch (IllegalArgumentException e) {
throw new IncompleteElementException("No cache for namespace '" + namespace + "' could be found.", e);
}
}
下面的方法是创建cache。
public Cache useNewCache(Class<? extends Cache> typeClass,
Class<? extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
boolean blocking,
Properties props) {
Cache cache = new CacheBuilder(currentNamespace)
.implementation(valueOrDefault(typeClass, PerpetualCache.class))
.addDecorator(valueOrDefault(evictionClass, LruCache.class))
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
configuration.addCache(cache);
currentCache = cache;
return cache;
}
其中有一个地方要弄清楚,就是Class<? extends Cache> typeClass, Class<? extends Cache> evictionClass的区别在哪里?在哪里呢?请看下图,一个是实现,一个是装饰者,你可以暂时理解为作用不同,就这么简单。

下面是addParameterMap方法的一些介绍。
public ParameterMap addParameterMap(String id, Class<?> parameterClass, List<ParameterMapping> parameterMappings) {
//得到包名。
id = applyCurrentNamespace(id, false);
//工厂方法创建参数Map,并添加到configuration中去。
ParameterMap parameterMap = new ParameterMap.Builder(configuration, id, parameterClass, parameterMappings).build();
configuration.addParameterMap(parameterMap);
return parameterMap;
}
下面是buildParameterMap的介绍,其实它也是利用了工厂方法骑构造。
public ParameterMapping buildParameterMapping(
Class<?> parameterType,
String property,
Class<?> javaType,
JdbcType jdbcType,
String resultMap,
ParameterMode parameterMode,
Class<? extends TypeHandler<?>> typeHandler,
Integer numericScale) {
resultMap = applyCurrentNamespace(resultMap, true); // Class parameterType = parameterMapBuilder.type();
Class<?> javaTypeClass = resolveParameterJavaType(parameterType, property, javaType, jdbcType);
TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler); return new ParameterMapping.Builder(configuration, property, javaTypeClass)
.jdbcType(jdbcType)
.resultMapId(resultMap)
.mode(parameterMode)
.numericScale(numericScale)
.typeHandler(typeHandlerInstance)
.build();
}
下面的是建立一个结果集,然后把结果集添加到configuration里面。
public ResultMap addResultMap(
String id,
Class<?> type,
String extend,
Discriminator discriminator,
List<ResultMapping> resultMappings,
Boolean autoMapping) {
id = applyCurrentNamespace(id, false);
extend = applyCurrentNamespace(extend, true); if (extend != null) {
if (!configuration.hasResultMap(extend)) {
throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");
}
ResultMap resultMap = configuration.getResultMap(extend);
List<ResultMapping> extendedResultMappings = new ArrayList<ResultMapping>(resultMap.getResultMappings());
extendedResultMappings.removeAll(resultMappings);
// Remove parent constructor if this resultMap declares a constructor.
boolean declaresConstructor = false;
for (ResultMapping resultMapping : resultMappings) {
if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {
declaresConstructor = true;
break;
}
}
if (declaresConstructor) {
Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator();
while (extendedResultMappingsIter.hasNext()) {
if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) {
extendedResultMappingsIter.remove();
}
}
}
resultMappings.addAll(extendedResultMappings);
}
ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)
.discriminator(discriminator)
.build();
configuration.addResultMap(resultMap);
return resultMap;
}
当然,下面的也太多了,就不一一介绍了,还有一些这些结果集的一些Getter方法;有兴趣的可以自己去看看,不过我们从这里得到了一个很重要的东西,那就是贯穿上下文的一个东西:Configuration!,这个东西可以说是无处不在,不管是在基类,还是在派生类中。
我们还看到了一些工厂的Relover,那这些resover类其实也是调用了上面的一些public方法而已,没啥特别的,真的。
下面我们再看看SqlSourceBuilder 这个类,这个先从string解析成map,然后再判断是否是sql类型,如果是,继续解析。
private ParameterMapping buildParameterMapping(String content) {
Map<String, String> propertiesMap = parseParameterMapping(content);
String property = propertiesMap.get("property");
Class<?> propertyType;
if (metaParameters.hasGetter(property)) { // issue #448 get type from additional params
propertyType = metaParameters.getGetterType(property);
} else if (typeHandlerRegistry.hasTypeHandler(parameterType)) {
propertyType = parameterType;
} else if (JdbcType.CURSOR.name().equals(propertiesMap.get("jdbcType"))) {
propertyType = java.sql.ResultSet.class;
} else if (property == null || Map.class.isAssignableFrom(parameterType)) {
propertyType = Object.class;
} else {
MetaClass metaClass = MetaClass.forClass(parameterType, configuration.getReflectorFactory());
if (metaClass.hasGetter(property)) {
propertyType = metaClass.getGetterType(property);
} else {
propertyType = Object.class;
}
}
ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, propertyType);
Class<?> javaType = propertyType;
String typeHandlerAlias = null;
for (Map.Entry<String, String> entry : propertiesMap.entrySet()) {
String name = entry.getKey();
String value = entry.getValue();
if ("javaType".equals(name)) {
javaType = resolveClass(value);
builder.javaType(javaType);
} else if ("jdbcType".equals(name)) {
builder.jdbcType(resolveJdbcType(value));
} else if ("mode".equals(name)) {
builder.mode(resolveParameterMode(value));
} else if ("numericScale".equals(name)) {
builder.numericScale(Integer.valueOf(value));
} else if ("resultMap".equals(name)) {
builder.resultMapId(value);
} else if ("typeHandler".equals(name)) {
typeHandlerAlias = value;
} else if ("jdbcTypeName".equals(name)) {
builder.jdbcTypeName(value);
} else if ("property".equals(name)) {
// Do Nothing
} else if ("expression".equals(name)) {
throw new BuilderException("Expression based parameters are not supported yet");
} else {
throw new BuilderException("An invalid property '" + name + "' was found in mapping #{" + content + "}. Valid properties are " + parameterProperties);
}
}
if (typeHandlerAlias != null) {
builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
}
return builder.build();
}
其中比较重要的就是下面的代码,下面做一个分析,首先会得到typeHanldler,然后再在buidler里对这个进行注册。
if (typeHandlerAlias != null) {
builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias));
}
return builder.build();
我们关键是看看builder.build方法,它是一个private的方法,它的作用就是get到我们开始设置的值,下面的validate方法也是做一些基础验证的,具体的可以略过,没啥价值。
private void resolveTypeHandler() {
if (parameterMapping.typeHandler == null && parameterMapping.javaType != null) {
Configuration configuration = parameterMapping.configuration;
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
parameterMapping.typeHandler = typeHandlerRegistry.getTypeHandler(parameterMapping.javaType, parameterMapping.jdbcType);
}
}
下面我们再来看看这个类:StaticSqlSource 其实我觉得这个玩意没啥用!真的不是贬低写mybatis的人,真没看出有什么用,具体看下面的代码。
public class StaticSqlSource implements SqlSource {
private final String sql;
private final List<ParameterMapping> parameterMappings;
private final Configuration configuration;
public StaticSqlSource(Configuration configuration, String sql) {
this(configuration, sql, null);
}
public StaticSqlSource(Configuration configuration, String sql, List<ParameterMapping> parameterMappings) {
this.sql = sql;
this.parameterMappings = parameterMappings;
this.configuration = configuration;
}
@Override
public BoundSql getBoundSql(Object parameterObject) {
return new BoundSql(configuration, sql, parameterMappings, parameterObject);
}
这些类同级的包里面,还有一个XML的包,里面包含的DTD文件,以及一些工具类,大家理解这些东西,其实就是为了把烦人的XML转换成一个可用的configuration对象的的工具类就行了,真的没必要深究。

关于builder的annotation 大家应该不陌生了吧?我介绍了这么多。构造函数说得很清楚了,其实把一些基本的注解加进了,CRUD而已。
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) {
String resource = type.getName().replace('.', '/') + ".java (best guess)";
this.assistant = new MapperBuilderAssistant(configuration, resource);
this.configuration = configuration;
this.type = type;
sqlAnnotationTypes.add(Select.class);
sqlAnnotationTypes.add(Insert.class);
sqlAnnotationTypes.add(Update.class);
sqlAnnotationTypes.add(Delete.class);
sqlProviderAnnotationTypes.add(SelectProvider.class);
sqlProviderAnnotationTypes.add(InsertProvider.class);
sqlProviderAnnotationTypes.add(UpdateProvider.class);
sqlProviderAnnotationTypes.add(DeleteProvider.class);
}
有一个核心方法,比较重要:parse,作用很明显,就是转换呗,然后是从configuration拿玩意,然后转换成有用的东西。其实这个不就是我们写的mapper类的XML文件吗?!用过mybatis的人都知道的。注释写了一点,不过更深入了,我觉得没必要写了,靠大家自己去发掘。
public void parse() {
String resource = type.toString();
if (!configuration.isResourceLoaded(resource)) {
//载入XML资源文件。
loadXmlResource();
//把资源文件添加到configuation里面。
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
//修改assistant变量
parseCache();
//修改assistant变量2
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();
}
Mybatis框架解析之Builder解析的更多相关文章
- Mybatis框架基础支持层——解析器模块(2)
解析器模块,核心类XPathParser /** * 封装了用于xml解析的类XPath.Document和EntityResolver */ public class XPathParser { / ...
- mybatis框架(3)---SqlMapConfig.xml解析
SqlMapConfig.xml SqlMapConfig.xml是Mybatis的全局配置参数,关于他的具体用的有专门的MyBatis - API文档,这里面讲的非常清楚,所以我这里就挑几个讲下: ...
- MyBatis框架的使用及源码分析(四) 解析Mapper接口映射xml文件
在<MyBatis框架中Mapper映射配置的使用及原理解析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder> 一文中,我们知道mybat ...
- mybatis 动态SQL 源码解析
摘要 mybatis是个人最新喜欢的半自动ORM框架,它实现了SQL和业务逻辑的完美分割,今天我们来讨论一个问题,mybatis 是如何动态生成SQL SqlSessionManager sqlSes ...
- Spring mybatis源码篇章-XMLLanguageDriver解析sql包装为SqlSource
前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(二) 首先了解下sql mapper的动态sql语法 具体的动态sql的 ...
- MyBatis 源码分析 - 配置文件解析过程
* 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...
- MyBatis官方教程及源代码解析——mapper映射文件
缓存 1.官方文档 MyBatis 包括一个非常强大的查询缓存特性,它能够非常方便地配置和定制. MyBatis 3 中的缓存实现的非常多改进都已经实现了,使得它更加强大并且易于配置. 默认情况下是没 ...
- Mybatis拦截器执行过程解析
上一篇文章 Mybatis拦截器之数据加密解密 介绍了 Mybatis 拦截器的简单使用,这篇文章将透彻的分析 Mybatis 是怎样发现拦截器以及调用拦截器的 intercept 方法的 小伙伴先按 ...
- mybatis源码配置文件解析之五:解析mappers标签(解析XML映射文件)
在上篇文章中分析了mybatis解析<mappers>标签,<mybatis源码配置文件解析之五:解析mappers标签>重点分析了如何解析<mappers>标签中 ...
随机推荐
- VS2015编译VS2013工程文件出错
错误:未能从程序集"C:\Program Files (x86)\MSBuild\14.0\bin\Microsoft.Build.Tasks.v14.0.dll"加载任务工厂&q ...
- AngularJS学习篇(十)
AngularJS Select(选择框) 使用 ng-options 创建选择框 在 AngularJS 中我们可以使用 ng-option 指令来创建一个下拉列表,列表项通过对象和数组循环输出,如 ...
- css 行内元素设置宽高
有2中实现方法: 1.设置display:block inline-block,使其width属性生效 2.如果设置float:left | right, 使其width属性生效. (浮动)使得指 ...
- 全排列Permutations
描述 Given a collection of numbers, return all possible permutations.For example,[1,2,3] have the foll ...
- redis源码分析之事务Transaction(上)
这周学习了一下redis事务功能的实现原理,本来是想用一篇文章进行总结的,写完以后发现这块内容比较多,而且多个命令之间又互相依赖,放在一篇文章里一方面篇幅会比较大,另一方面文章组织结构会比较乱,不容易 ...
- C#实现倒油算法
原题如下:12(a桶 满的 有12斤油)斤桶里 取出6斤油 有 另外有8斤(b桶)和5斤(c桶)两个空桶 让程序输出取出这6斤油的步骤 现在实现的算法可以配参数(定义有几个桶,初始有多少油,要得到多 ...
- [转载] 使用HTML5的十大原因
转载自http://www.williamlong.info/archives/3024.html 你难道还没有考虑使用HTML5? 当然我猜想你可能有自己的原因:它现在还没有被广泛的支持,在IE中不 ...
- Scratch——教小孩子学编码
教小孩子学编码 http://scratch.mit.edu/ http://v.163.com/movie/2013/3/H/I/M92389L06_M9238GTHI.html
- FPGA时钟分频(转)
http://www.cnblogs.com/fpga/archive/2009/10/24/1589318.html 占空比为50%的分频 偶数分频比较简单 比如N分频,那么计数到N/2-1,然后时 ...
- python学习笔记——Day 1
python是一种什么语言? 编译与解释 编译器是把源程序的每一条语句都编译成机器语言,并保存成二进制文件,这样运行时计算机可以直接以机器语言来运行此程序,速度很快; 而解释器则是只在执行程序时,才一 ...