首先我们从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解析的更多相关文章

  1. Mybatis框架基础支持层——解析器模块(2)

    解析器模块,核心类XPathParser /** * 封装了用于xml解析的类XPath.Document和EntityResolver */ public class XPathParser { / ...

  2. mybatis框架(3)---SqlMapConfig.xml解析

    SqlMapConfig.xml SqlMapConfig.xml是Mybatis的全局配置参数,关于他的具体用的有专门的MyBatis - API文档,这里面讲的非常清楚,所以我这里就挑几个讲下: ...

  3. MyBatis框架的使用及源码分析(四) 解析Mapper接口映射xml文件

    在<MyBatis框架中Mapper映射配置的使用及原理解析(二) 配置篇 SqlSessionFactoryBuilder,XMLConfigBuilder> 一文中,我们知道mybat ...

  4. mybatis 动态SQL 源码解析

    摘要 mybatis是个人最新喜欢的半自动ORM框架,它实现了SQL和业务逻辑的完美分割,今天我们来讨论一个问题,mybatis 是如何动态生成SQL SqlSessionManager sqlSes ...

  5. Spring mybatis源码篇章-XMLLanguageDriver解析sql包装为SqlSource

    前言:通过阅读源码对实现机制进行了解有利于陶冶情操,承接前文Spring mybatis源码篇章-MybatisDAO文件解析(二) 首先了解下sql mapper的动态sql语法 具体的动态sql的 ...

  6. MyBatis 源码分析 - 配置文件解析过程

    * 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...

  7. MyBatis官方教程及源代码解析——mapper映射文件

    缓存 1.官方文档 MyBatis 包括一个非常强大的查询缓存特性,它能够非常方便地配置和定制. MyBatis 3 中的缓存实现的非常多改进都已经实现了,使得它更加强大并且易于配置. 默认情况下是没 ...

  8. Mybatis拦截器执行过程解析

    上一篇文章 Mybatis拦截器之数据加密解密 介绍了 Mybatis 拦截器的简单使用,这篇文章将透彻的分析 Mybatis 是怎样发现拦截器以及调用拦截器的 intercept 方法的 小伙伴先按 ...

  9. mybatis源码配置文件解析之五:解析mappers标签(解析XML映射文件)

    在上篇文章中分析了mybatis解析<mappers>标签,<mybatis源码配置文件解析之五:解析mappers标签>重点分析了如何解析<mappers>标签中 ...

随机推荐

  1. hadoop2集群中关键配置文件的记录

    配置HDFS  高可用 1.配置HDFS配置文件 $ vi hdfs-site.xml #写入 <configuration> #配置NameService 名字随便起 <prope ...

  2. I - Intersection HDU - 5120(圆环相交面积)

    Matt is a big fan of logo design. Recently he falls in love with logo made up by rings. The followin ...

  3. poj 3340 Barbara Bennett's Wild Numbers(数位DP)

    Barbara Bennett's Wild Numbers Time Limit: 2000MS   Memory Limit: 65536K Total Submissions: 3153   A ...

  4. CUDA C Best Practices Guide 在线教程学习笔记 Part 1

    0. APOD过程 ● 评估.分析代码运行时间的组成,对瓶颈进行并行化设计.了解需求和约束条件,确定应用程序的加速性能改善的上限. ● 并行化.根据原来的代码,采用一些手段进行并行化,例如使用现有库, ...

  5. MSBuild Tools解决办法

    每次在CI上通过Msbuild做发布,基本都会碰到下面的问题 error MSB4019: 未找到导入的项目"C:\Program Files (x86)\MSBuild\Microsoft ...

  6. Easy UI下拉列表默认选中(多行)与为文本框赋值

    1.为单行文本框赋值 var data2 = $('#LoadArea').combobox("getData"); if (data2) { $('#id).combobox(' ...

  7. 表空间与数据文件Offline,online的区别

    首先明确,表空间与数据文件的关系:Oracle数据库表空间有两种,一种smallfile小文件表空间(默认),另一种bigfile大文件表空间: 默认表空间与数据文件的关系:允许一对多的处理方式,一个 ...

  8. 逆向知识第八讲,if语句在汇编中表达的方式

    逆向知识第八讲,if语句在汇编中表达的方式 一丶if else的最简单情况还原(无分支情况) 高级代码: #include "stdafx.h" int main(int argc ...

  9. JavaScript 数组基本操作

    简介 数组操作无论是在JavaScript中还是在其他语言中都是很常见.经常会用到的,现在我把JavaScript 数组基本操作整理一下,供大家参考学习.里边包含我自己的见解以及案例,希望能够帮助大家 ...

  10. Velocity(2)——常用语法

    Velocity是一个基于java的模板引擎(template engine),它允许任何人仅仅简单的使用模板语言(template language)来引用由java代码定义的对象.作为一个比较完善 ...