首先咱們從builder這個類入手,首先咱們注意到BaseBuilder,其實它的本質上市一個抽象類,它從本質上抽象出了Builder的一切,我猜測這裏必定使用了建造者模式,可是這個抽象類裏面竟然沒有抽象方法!java
其中XXXValueOf方法,實際上是把String字符串轉換成了相對應的類型,以下代碼。sql
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轉換成相對應的類型。express
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); } }
下面的方法是經過字符串別名解析出相對應的類型,再從類型建立實例。mybatis
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裏建立一個新的。app
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,下面對此類作一個解析,好比下面的就是解析命名空間的,就是包名。ide
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的區別在哪裏?在哪裏呢?請看下圖,一個是實現,一個是裝飾者,你能夠暫時理解爲做用不一樣,就這麼簡單。ui
下面是addParameterMap方法的一些介紹。this
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(); }