首先我們從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(); }
