上圖是sqlsource在mybatis中建立的時序圖;node
如下會經過源碼的方式將時序圖進行深刻的說明;sql
1.XMLStatementBuilder對xxxMapper.xml中對每一個CURD進行解析成MappedStatement對象express
如:apache
<select id="selectRawWithInclude" resultType="Name" lang="raw"> SELECT firstName, lastName <include refid="include"/> WHERE lastName LIKE #{name} </select>
lang屬性指定該MapperStatement中的sqlSource屬性是由哪類LanguageDriver進行markdown
public interface LanguageDriver { /** * 建立ParameterHandler,將入參的數據傳入jdbc statement ex:PrepareStatement中的 '?' */ ParameterHandler createParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql); /** * 經過解析xml中的curd node數據建立SqlSource */ SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType); /** * 經過解析Mapper interface method上的註解數據建立SqlSource */ SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType); } lang=xml -> XMLLanguageDriver:建立動態sql的LanguageDriver lang=raw -> RawLanguageDriver:建立靜態sql的LanguageDriver 在解析MapperStatement時,經過typeAliasRegistry解析lang值獲取LanguageDriver; private LanguageDriver getLanguageDriver(String lang) { Class<? extends LanguageDriver> langClass = null; if (lang != null) { langClass = resolveClass(lang); } return configuration.getLanguageDriver(langClass); } === protected <T> Class<? extends T> resolveClass(String alias) { if (alias == null) { return null; } try { return resolveAlias(alias); } catch (Exception e) { throw new BuilderException("Error resolving class. Cause: " + e, e); } } === protected <T> Class<? extends T> resolveAlias(String alias) { return typeAliasRegistry.resolveAlias(alias); } === 而後由Configuration中的languageRegistry獲取LanguageDriver對象 public LanguageDriver getLanguageDriver(Class<? extends LanguageDriver> langClass) { if (langClass == null) { return languageRegistry.getDefaultDriver(); } languageRegistry.register(langClass); return languageRegistry.getDriver(langClass); } languageRegistry初始化 typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class); typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
2.調用XMLScriptBuilder解析sql語句,將trim/where/set/foreach/if/choose/when/otherwise/bind標籤解析成相應的SqlNode對象,最後由MixedSqlNode進行封裝;mybatis
public class MixedSqlNode implements SqlNode { private final List<SqlNode> contents; public MixedSqlNode(List<SqlNode> contents) { this.contents = contents; } @Override public boolean apply(DynamicContext context) { contents.forEach(node -> node.apply(context)); return true; } }
將sql語句中的標籤經過實現NodeHandler接口的類生成SqlNode對象;由MixedSqlNode對象進行封裝;app
XMLScriptBuilder類很精簡,初始化sql 標籤的map;定義生成SqlNode的NodeHandler接口ide
nodeHandlerMap.put("trim", new TrimHandler()); nodeHandlerMap.put("where", new WhereHandler()); nodeHandlerMap.put("set", new SetHandler()); nodeHandlerMap.put("foreach", new ForEachHandler()); nodeHandlerMap.put("if", new IfHandler()); nodeHandlerMap.put("choose", new ChooseHandler()); nodeHandlerMap.put("when", new IfHandler()); nodeHandlerMap.put("otherwise", new OtherwiseHandler()); nodeHandlerMap.put("bind", new BindHandler()); private interface NodeHandler { void handleNode(XNode nodeToHandle, List<SqlNode> targetContents); }
3.將sql語句中的標籤進行解析時,判斷是不是動態語句函數
XMLScriptBuilder# public SqlSource parseScriptNode() { MixedSqlNode rootSqlNode = parseDynamicTags(context); SqlSource sqlSource; if (isDynamic) { sqlSource = new DynamicSqlSource(configuration, rootSqlNode); } else { sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType); } return sqlSource; }
若是是動態的則返回DynamicSqlSource,反之則返回RawSqlSource學習
4/5/6.MappStatement對象中包含屬性SqlSource;
7.在調用SqlSession.select方法時,調用MappedStatement.getBoundSql(parameterObject)獲取BoundSql對象
MappedStatement# public BoundSql getBoundSql(Object parameterObject) { BoundSql boundSql = sqlSource.getBoundSql(parameterObject); ... return boundSql; }
DynamicSqlSource# public BoundSql getBoundSql(Object parameterObject) { DynamicContext context = new DynamicContext(configuration, parameterObject); rootSqlNode.apply(context); SqlSourceBuilder sqlSourceParser = new SqlSourceBuilder(configuration); Class<?> parameterType = parameterObject == null ? Object.class : parameterObject.getClass(); SqlSource sqlSource = sqlSourceParser.parse(context.getSql(), parameterType, context.getBindings()); BoundSql boundSql = sqlSource.getBoundSql(parameterObject); context.getBindings().forEach(boundSql::setAdditionalParameter); return boundSql; }
1.建立DynamicContext對象
2.調用rootSqlNode.apply(context)方法,將SqlNode對象的apply方法,將sql動態語句轉成靜態語句
3.將解析後的sql、入參類型、context.getBindings()值傳入SqlSourceBuilder,進行解析返回StaticSqlSource
4.傳入入參對象,返回boundSql
8.調用SqlSourceBuilder.parse方法,
SqlSourceBuilder# public SqlSource parse(String originalSql, Class<?> parameterType, Map<String, Object> additionalParameters) { ParameterMappingTokenHandler handler = new ParameterMappingTokenHandler(configuration, parameterType, additionalParameters); GenericTokenParser parser = new GenericTokenParser("#{", "}", handler); String sql = parser.parse(originalSql); return new StaticSqlSource(configuration, sql, handler.getParameterMappings()); }
針對#{id, typeHandler=org.apache.ibatis.submitted.uuid_test.UUIDTypeHandler}這種狀況,
1.將#{}的內容轉化爲ParameterExpression對象
2.將additionalParameters中的值轉換爲MetaObject;經過屬性名獲取屬性的類型
3.經過根據類型判斷是否有類型處理器
4.將對象中的內容進行解析,轉換爲ParameterMapping
經過以上方式,將動態sql語句中的#{}值轉化爲ParameterMapping;進行生成StaticSqlSource對象
9/10.調用StaticSqlSource.getBoundSql方法;
public class StaticSqlSource implements SqlSource { ... @Override public BoundSql getBoundSql(Object parameterObject) { return new BoundSql(configuration, sql, parameterMappings, parameterObject); } }
以上流程進行總結:
1.經過lang屬性從languageRegistry中獲取LanguageDriver,languageDriver經過xml or annotation解析獲取DynamicSqlSource or RawSqlSource
2.以DynamicSqlSource爲例,對sql語句中對標籤轉化爲SqlNode對象,而後進行解析,將解析後的數據存放於DynamicContext
3.根據sql語句中的#{}值解析成ParameterMapping,並以configuration、sql(解析後的靜態語句)、值的映射(ParameterMapping)組裝StaticSqlSource
4.最後轉化爲BoundSql對象
================================================================
在學習的過程當中,DynamicContext比較有意思,如今說一下這個類
DynamicContext類是在執行多個sqlNode的入參,將每一個SqlNode.apply方法執行完的數據存入於DynamicContext;
DynamicContext的構造函數 public DynamicContext(Configuration configuration, Object parameterObject) { if (parameterObject != null && !(parameterObject instanceof Map)) { MetaObject metaObject = configuration.newMetaObject(parameterObject); boolean existsTypeHandler = configuration.getTypeHandlerRegistry().hasTypeHandler(parameterObject.getClass()); bindings = new ContextMap(metaObject, existsTypeHandler); } else { bindings = new ContextMap(null, false); } bindings.put(PARAMETER_OBJECT_KEY, parameterObject); bindings.put(DATABASE_ID_KEY, configuration.getDatabaseId()); }
將解析過程當中產生的屬性(如:ForEachSqlNode)存放於bindings,bindings繼承於HashMap
並且給bindings注入了propertyAccessor
static { OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor()); }
當獲取contextMap中當元素時,會進入到ContextAccessor中先進行獲取,如獲取屬性的切面操做,對執行方法也是能夠的,注入methodAccessor
static class ContextAccessor implements PropertyAccessor { @Override public Object getProperty(Map context, Object target, Object name) { Map map = (Map) target; Object result = map.get(name); if (map.containsKey(name) || result != null) { return result; } Object parameterObject = map.get(PARAMETER_OBJECT_KEY); if (parameterObject instanceof Map) { return ((Map)parameterObject).get(name); } return null; } ... }
獲取方法
public static Object getValue(String expression, Object root) { try { Map context = Ognl.createDefaultContext(root, MEMBER_ACCESS, CLASS_RESOLVER, null); return Ognl.getValue(parseExpression(expression), context, root); } catch (OgnlException e) { throw new BuilderException("Error evaluating expression '" + expression + "'. Cause: " + e, e); } }
建立OgnlContext,將expression進行解析成property,而後會調用到自定義的PropertyAccessor中
Ognl其實主要功能是對集合、類對象屬性表達式的解析,如:
ContextMap bindings = new ContextMap(null, false); OgnlRuntime.setPropertyAccessor(ContextMap.class, new ContextAccessor()); bindings.put("xxx", "zzz"); bindings.put("yyy", "aaa"); OgnlContext context = new OgnlContext(null,null,new DefaultMemberAccess(true)); context.put("bindings", bindings); Object obj = Ognl.getValue("#bindings.xxx", context, context.getRoot()); System.out.println(obj); // output: "zzz"
方法也是如此,當注入類MethodAccessor時,當經過表達式「bindings.put()」,也會執行MethodAccessor關於put方法的切面