Mybatis之Configuration初始化(配置文件.xml的解析)


源碼解讀第一步我覺着應該從Mybatis如何解析配置文件開始。

1.先不看跟Spring集成如何解析,先看從SqlSessionFactoryBuilder如果解析的。

1 String resouce = "conf.xml";
2 InputStream is = Resources.getResourceAsStream(resouce);
3 
4 // 構建sqlSession工廠
5 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(is);

SqlSessionFactoryBuilder

 1   public SqlSessionFactory build(InputStream inputStream) {
 2     return build(inputStream, null, null);
 3   }
 4 
 5   public SqlSessionFactory build(InputStream inputStream, String environment) {
 6     return build(inputStream, environment, null);
 7   }
 8 
 9   public SqlSessionFactory build(InputStream inputStream, Properties properties) {
10     return build(inputStream, null, properties);
11   }
12   //上面那么多同名不同參的方法最后都會進入這個方法
   //支持傳入Enviromment.properties實際上是支持動態傳入覆蓋全局配置xml的內容 13 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) { 14 try { 15 XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
      //因為從下面的Build方法可以看出 parser.parse()后Configuration就初始化好了
16 return build(parser.parse()); 17 } catch (Exception e) { 18 throw ExceptionFactory.wrapException("Error building SqlSession.", e); 19 } finally { 20 ErrorContext.instance().reset(); 21 try { 22 inputStream.close(); 23 } catch (IOException e) { 24 // Intentionally ignore. Prefer previous error. 25 } 26 } 27 }
   public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
   }

真正初始化Configuration的類是XMLConfigBuilder

 1   public Configuration parse() {
    //這一塊可以看出來 全局Configuration只會初始化一次,實例化時候是false
2 if (parsed) { 3 throw new BuilderException("Each XMLConfigBuilder can only be used once."); 4 } 5 parsed = true; 6 parseConfiguration(parser.evalNode("/configuration")); 7 return configuration; 8 } 9 //獲取配置文件整個/configuration節點內容 10 private void parseConfiguration(XNode root) { 11 try { 12 Properties settings = settingsAsPropertiess(root.evalNode("settings")); //初始化配置文件中全局變量 13 //issue #117 read properties first 14 propertiesElement(root.evalNode("properties"));//初始化xml中的配置文件,同時講其中的變量保存起來,因為可能其他地方引用 15 loadCustomVfs(settings); //加載自定義的VFS實現類? 這個什么用? 16 typeAliasesElement(root.evalNode("typeAliases")); //加載typeAliases別名初始化 17 pluginElement(root.evalNode("plugins")); //加載插件,實際上就是攔截器 18 objectFactoryElement(root.evalNode("objectFactory"));// //加載自定義的對象工廠 19 objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); //加載自定義的處理駝峰方式的key的處理器 20 reflectionFactoryElement(root.evalNode("reflectionFactory")); //加載自定義的反射器 沒用過? 21 settingsElement(settings); //將setting的屬性,設置到Configuration對象屬性中 22 // read it after objectFactory and objectWrapperFactory issue #631 23 environmentsElement(root.evalNode("environments")); 24 databaseIdProviderElement(root.evalNode("databaseIdProvider")); 25 typeHandlerElement(root.evalNode("typeHandlers")); //初始化類型處理器 26 mapperElement(root.evalNode("mappers")); //處理mappers節點內容,實際上就是初始化MaperStatement 27 } catch (Exception e) { 28 throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); 29 } 30 }

因為大部分方法都比較簡單,我這里只介紹幾個我認為比較重要的。

① typeAliasesElement(root.evalNode("typeAliases")); //加載typeAliases別名初始化

 1   private void typeAliasesElement(XNode parent) {
 2     if (parent != null) {
 3       for (XNode child : parent.getChildren()) {
 4         if ("package".equals(child.getName())) { //配置的是包名 掃描包里所有的類。放入的key默認是注解的key
 5           String typeAliasPackage = child.getStringAttribute("name");
 6           configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
 7         } else {
 8           String alias = child.getStringAttribute("alias");
 9           String type = child.getStringAttribute("type");
10           try {
11             Class<?> clazz = Resources.classForName(type);
12             if (alias == null) {
13               typeAliasRegistry.registerAlias(clazz);
14             } else {
15               typeAliasRegistry.registerAlias(alias, clazz);
16             }
         //實際上就是存在了TypeAliasRegistry類的一個私有map里面。
17 } catch (ClassNotFoundException e) { 18 throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e); 19 } 20 } 21 } 22 } 23 }
1 public class TypeAliasRegistry {
2 
3   private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();
  。。。。。。
4 }

 

② pluginElement(root.evalNode("plugins")); 加載插件,便於后期理解攔截器原理

 1   private void pluginElement(XNode parent) throws Exception {
 2     if (parent != null) {
 3       for (XNode child : parent.getChildren()) {
 4         String interceptor = child.getStringAttribute("interceptor");
 5         Properties properties = child.getChildrenAsProperties();
       //獲取interceptor 這里resolveClass實際上就是到TypeAliasResitstry里面找一下,找到了獲取class,沒有直接用class去反射回去對象
6 Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance(); 7 interceptorInstance.setProperties(properties);
       //獲取對象后調用configuration方法。
8 configuration.addInterceptor(interceptorInstance); 9 } 10 } 11 }

Configuration

1  public void addInterceptor(Interceptor interceptor) {
2     interceptorChain.addInterceptor(interceptor);
3   }

InterceptorChain

1   public void addInterceptor(Interceptor interceptor) {
2     interceptors.add(interceptor);
3   }

這一塊就是放Configuration的攔截器鏈里面添加攔截器。這一塊現在知道是在這時候添加的就好了。后面介紹Mybatis的攔截器的時候深入了解。

③ mapperElement(root.evalNode("mappers")); //處理mappers節點內容,實際上就是初始化MaperStatement

 1   private void mapperElement(XNode parent) throws Exception {
 2     if (parent != null) {
 3       for (XNode child : parent.getChildren()) {
 4         if ("package".equals(child.getName())) { 
 5           String mapperPackage = child.getStringAttribute("name");
 6           configuration.addMappers(mapperPackage);
 7         } else {
 8           String resource = child.getStringAttribute("resource");
 9           String url = child.getStringAttribute("url");
10           String mapperClass = child.getStringAttribute("class");
        //xml文件中mapper節點,配置resource,url是執行的mapper.xml文件的位置,所以現在只分析這一塊。
11 if (resource != null && url == null && mapperClass == null) { 12 ErrorContext.instance().resource(resource); 13 InputStream inputStream = Resources.getResourceAsStream(resource); 14 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); 15 mapperParser.parse(); 16 } else if (resource == null && url != null && mapperClass == null) { 17 ErrorContext.instance().resource(url); 18 InputStream inputStream = Resources.getUrlAsStream(url); 19 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); 20 mapperParser.parse(); 21 } else if (resource == null && url == null && mapperClass != null) { 22 Class<?> mapperInterface = Resources.classForName(mapperClass); 23 configuration.addMapper(mapperInterface); 24 } else { 25 throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); 26 } 27 } 28 } 29 } 30 }

可以看出來resource和url都是通過XMLMapperBuilder來解析的。下面來看下parse方法。

 1   private XMLMapperBuilder(XPathParser parser, Configuration configuration, String resource, Map<String, XNode> sqlFragments) {
 2     super(configuration);
//前面還有個構造器,是根據傳入的inputstream構造XPathParser,parser可以拿到resouce里面的內容
3 this.builderAssistant = new MapperBuilderAssistant(configuration, resource); 4 this.parser = parser; 5 this.sqlFragments = sqlFragments; 6 this.resource = resource; 7 } 8 9 public void parse() { 10 if (!configuration.isResourceLoaded(resource)) { 是否加載過改文件, 11 configurationElement(parser.evalNode("/mapper")); 來解析mapper文件內容 12 configuration.addLoadedResource(resource); 添加加載記錄 13 bindMapperForNamespace();往configration添加命名空間的代理對象 14 } 15 16 parsePendingResultMaps(); 17 parsePendingChacheRefs(); 18 parsePendingStatements(); 19 }
 1   private void configurationElement(XNode context) { //解析mapper.xml子節點的內容
 2     try {
 3       String namespace = context.getStringAttribute("namespace");
 4       if (namespace == null || namespace.equals("")) {
 5         throw new BuilderException("Mapper's namespace cannot be empty");
 6       }
 7       builderAssistant.setCurrentNamespace(namespace);
 8       cacheRefElement(context.evalNode("cache-ref"));
 9       cacheElement(context.evalNode("cache"));
10       parameterMapElement(context.evalNodes("/mapper/parameterMap"));
11       resultMapElements(context.evalNodes("/mapper/resultMap")); //解析resultMap 很復雜,后面單獨解讀
12       sqlElement(context.evalNodes("/mapper/sql"));
13       buildStatementFromContext(context.evalNodes("select|insert|update|delete")); //初始化這幾個類型節點的內容
14     } catch (Exception e) {
15       throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
16     }
17   }
 1   private void buildStatementFromContext(List<XNode> list) {
因為會有多個節點,所以是一個List<XNode>
2 if (configuration.getDatabaseId() != null) { 3 buildStatementFromContext(list, configuration.getDatabaseId()); 4 } 5 buildStatementFromContext(list, null); 6 } 7 8 private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) { 9 for (XNode context : list) { 10 final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId); 11 try { 12 statementParser.parseStatementNode(); 13 } catch (IncompleteElementException e) { 14 configuration.addIncompleteStatement(statementParser); 15 } 16 } 17 }

從上面可以看出來最終是由XMLStatementBuilder來解析我們的寫的sql部分。

 1   public void parseStatementNode() {
 2     String id = context.getStringAttribute("id");
 3     String databaseId = context.getStringAttribute("databaseId");
 4 
 5     if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
 6       return;
 7     }
 8 
 9     Integer fetchSize = context.getIntAttribute("fetchSize");
10     Integer timeout = context.getIntAttribute("timeout");
11     String parameterMap = context.getStringAttribute("parameterMap");
12     String parameterType = context.getStringAttribute("parameterType");
13     Class<?> parameterTypeClass = resolveClass(parameterType);
14     String resultMap = context.getStringAttribute("resultMap");
15     String resultType = context.getStringAttribute("resultType");
    //獲取語言驅動, 我們自定義的語言驅動也是從這里讀取的, 同時參數處理器也是從驅動定義的,自定義參數處理器也可以在自定義語言驅動里面去實現
16 String lang = context.getStringAttribute("lang"); 17 LanguageDriver langDriver = getLanguageDriver(lang); 18 19 Class<?> resultTypeClass = resolveClass(resultType); 20 String resultSetType = context.getStringAttribute("resultSetType"); 21 StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString())); 22 ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType); 23 24 String nodeName = context.getNode().getNodeName(); 25 SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH)); 26 boolean isSelect = sqlCommandType == SqlCommandType.SELECT; 27 boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect); 28 boolean useCache = context.getBooleanAttribute("useCache", isSelect); 29 boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false); 30 31 // Include Fragments before parsing 32 XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant); 33 includeParser.applyIncludes(context.getNode()); 34 35 // Parse selectKey after includes and remove them. 36 processSelectKeyNodes(id, parameterTypeClass, langDriver); 37 //根據前面的語言驅動去獲取對應的SqlSource。SqlSource有兩種,一種是處理${}一種是處理#{} 38 // Parse the SQL (pre: <selectKey> and <include> were parsed and removed) 39 SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass); 40 String resultSets = context.getStringAttribute("resultSets"); 41 String keyProperty = context.getStringAttribute("keyProperty"); 42 String keyColumn = context.getStringAttribute("keyColumn"); 43 KeyGenerator keyGenerator; 44 String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX; 45 keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true); 46 if (configuration.hasKeyGenerator(keyStatementId)) { 47 keyGenerator = configuration.getKeyGenerator(keyStatementId); 48 } else { 49 keyGenerator = context.getBooleanAttribute("useGeneratedKeys", 50 configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType)) 51 ? new Jdbc3KeyGenerator() : new NoKeyGenerator(); 52 } 53   獲取節點所有屬性內容,調用builderAssistant,實現是MapperBuilderAssistant 在XmlMapperBuilder構造器初始化時候就制定了 54 builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, 55 fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, 56 resultSetTypeEnum, flushCache, useCache, resultOrdered, 57 keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets); 58 }
 1   public MappedStatement addMappedStatement(
 2       String id,
 3       SqlSource sqlSource,
 4       StatementType statementType,
 5       SqlCommandType sqlCommandType,
 6       Integer fetchSize,
 7       Integer timeout,
 8       String parameterMap,
 9       Class<?> parameterType,
10       String resultMap,
11       Class<?> resultType,
12       ResultSetType resultSetType,
13       boolean flushCache,
14       boolean useCache,
15       boolean resultOrdered,
16       KeyGenerator keyGenerator,
17       String keyProperty,
18       String keyColumn,
19       String databaseId,
20       LanguageDriver lang,
21       String resultSets) {
22 
23     if (unresolvedCacheRef) {
24       throw new IncompleteElementException("Cache-ref not yet resolved");
25     }
26 
27     id = applyCurrentNamespace(id, false); //把id加上namespace+"."
28     boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
29 
30     MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
31         .resource(resource)
32         .fetchSize(fetchSize)
33         .timeout(timeout)
34         .statementType(statementType)
35         .keyGenerator(keyGenerator)
36         .keyProperty(keyProperty)
37         .keyColumn(keyColumn)
38         .databaseId(databaseId)
39         .lang(lang)
40         .resultOrdered(resultOrdered)
41         .resulSets(resultSets)
42         .resultMaps(getStatementResultMaps(resultMap, resultType, id))
43         .resultSetType(resultSetType)
44         .flushCacheRequired(valueOrDefault(flushCache, !isSelect))
45         .useCache(valueOrDefault(useCache, isSelect))
46         .cache(currentCache);
47 
48     ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
49     if (statementParameterMap != null) {
50       statementBuilder.parameterMap(statementParameterMap);
51     }
52 
53     MappedStatement statement = statementBuilder.build();
54     configuration.addMappedStatement(statement);
55     return statement;
56   }
MappedStatement.Builder 是MappedStatement的內部類。里面有MappedStatement的引用,所有方法都設置內部引用mappedStatement的屬性,並返回自身,所以這一塊就是類似於setparam()
最終通過build方法 返回對象。 然后調用Congifuration.addMappedStatement保存到Congifuration對象里面。
1   public void addMappedStatement(MappedStatement ms) {
2     mappedStatements.put(ms.getId(), ms);
3   }

到此Congifuration對象初始話完事了...  全局只有一個。 

回過頭來剛才還有怎么獲取的語言驅動,和MappedStatement的SqlSource怎么初始化的沒說。

①獲取語言驅動:

16     String lang = context.getStringAttribute("lang");
17     LanguageDriver langDriver = getLanguageDriver(lang);
1   private LanguageDriver getLanguageDriver(String lang) {
2     Class<?> langClass = null;
3     if (lang != null) {
     // 也是到別名注冊器里面去找
4 langClass = resolveClass(lang); 5 }
構造助手類去獲取的。下面看MapperBuilderAssistant類怎么實現的
6 return builderAssistant.getLanguageDriver(langClass); 7 }
1   // 從configuration里面找,也有個語言注冊器,然后還有個默認的,我們平時不指定的時候都是默認的。 
  public LanguageDriver getLanguageDriver(Class<?> langClass) { 2 if (langClass != null) { 3 configuration.getLanguageRegistry().register(langClass); 4 } else {
      
5 langClass = configuration.getLanguageRegistry().getDefaultDriverClass(); 6 } 7 return configuration.getLanguageRegistry().getDriver(langClass); 8 }

那語言注冊器是什么時候注冊的呢。 來看Configuration的構造器

 1   public Configuration() {
 2     typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
 3     typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
 4 
 5     typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
 6     typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
 7     typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
 8 
 9     typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
10     typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
11     typeAliasRegistry.registerAlias("LRU", LruCache.class);
12     typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
13     typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
14 
15     typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
16 
17     typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
18     typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
19 
20     typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
21     typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
22     typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
23     typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
24     typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
25     typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
26     typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
27 
28     typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
29     typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
30     //在最下面 看到了嗎。默認的是XMLLanguageDruiver,然后把Raw也注冊進去了
31     languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
32     languageRegistry.register(RawLanguageDriver.class);
33   }

②MappedStatement的SqlSource怎么初始化

SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
因為我們一般都不配置lang屬性,所以看默認的XMLLanguageDriver怎么實現的。
1   @Override
2   public SqlSource createSqlSource(Configuration configuration, XNode script, Class<?> parameterType) {
3     XMLScriptBuilder builder = new XMLScriptBuilder(configuration, script, parameterType);
4     return builder.parseScriptNode();
5   }
XMLScriptBuilder 
 1   public SqlSource parseScriptNode() {
//主要在這里面判斷是否有Dynamic標簽 是就是就是${}
2 List<SqlNode> contents = parseDynamicTags(context); 3 MixedSqlNode rootSqlNode = new MixedSqlNode(contents); 4 SqlSource sqlSource = null; 5 if (isDynamic) { 6 sqlSource = new DynamicSqlSource(configuration, rootSqlNode); 7 } else { 8 sqlSource = new RawSqlSource(configuration, rootSqlNode, parameterType); 9 } 10 return sqlSource; 11 }
 1   List<SqlNode> parseDynamicTags(XNode node) {
 2     List<SqlNode> contents = new ArrayList<SqlNode>();
 3     NodeList children = node.getNode().getChildNodes();
 4     for (int i = 0; i < children.getLength(); i++) {
 5       XNode child = node.newXNode(children.item(i));
 6       if (child.getNode().getNodeType() == Node.CDATA_SECTION_NODE || child.getNode().getNodeType() == Node.TEXT_NODE) {
 7         String data = child.getStringBody("");
//獲取每一個node,然后判斷是否是isDynamic();
8 TextSqlNode textSqlNode = new TextSqlNode(data); 9 if (textSqlNode.isDynamic()) { 10 contents.add(textSqlNode); 11 isDynamic = true; 12 } else { 13 contents.add(new StaticTextSqlNode(data)); 14 } 15 } else if (child.getNode().getNodeType() == Node.ELEMENT_NODE) { // issue #628 16 String nodeName = child.getNode().getNodeName(); 17 NodeHandler handler = nodeHandlers(nodeName); 18 if (handler == null) { 19 throw new BuilderException("Unknown element <" + nodeName + "> in SQL statement."); 20 } 21 handler.handleNode(child, contents); 22 isDynamic = true; 23 } 24 } 25 return contents; 26 }
 1   public boolean isDynamic() {
//這一塊就是典型的面向接口編程, checker接口的實現類有很多, 在parser.parse一定會調用checker實現類的方法。
2 DynamicCheckerTokenParser checker = new DynamicCheckerTokenParser(); 3 GenericTokenParser parser = createParser(checker); 4 parser.parse(text); 5 return checker.isDynamic(); 6 } 7 8 private GenericTokenParser createParser(TokenHandler handler) { 9 return new GenericTokenParser("${", "}", handler); 10 }
 1 public String parse(String text) {
 2     final StringBuilder builder = new StringBuilder();
 3     final StringBuilder expression = new StringBuilder();
 4     if (text != null && text.length() > 0) {
 5       char[] src = text.toCharArray();
 6       int offset = 0;
 7       // search open token
 8       int start = text.indexOf(openToken, offset);
 9       while (start > -1) {
10         if (start > 0 && src[start - 1] == '\\') {
11           // this open token is escaped. remove the backslash and continue.
12           builder.append(src, offset, start - offset - 1).append(openToken);
13           offset = start + openToken.length();
14         } else {
15           // found open token. let's search close token.
16           expression.setLength(0);
17           builder.append(src, offset, start - offset);
18           offset = start + openToken.length();
19           int end = text.indexOf(closeToken, offset);
20           while (end > -1) {
21             if (end > offset && src[end - 1] == '\\') {
22               // this close token is escaped. remove the backslash and continue.
23               expression.append(src, offset, end - offset - 1).append(closeToken);
24               offset = end + closeToken.length();
25               end = text.indexOf(closeToken, offset);
26             } else {
27               expression.append(src, offset, end - offset);
28               offset = end + closeToken.length();
29               break;
30             }
31           }
32           if (end == -1) {
33             // close token was not found.
34             builder.append(src, start, src.length - start);
35             offset = src.length;
36           } else {
//這一塊實際上就是遍歷node文本找到openToken和cloaseToken中間的變量名字傳給handler處理
37 builder.append(handler.handleToken(expression.toString())); 38 offset = end + closeToken.length(); 39 } 40 } 41 start = text.indexOf(openToken, offset); 42 } 43 if (offset < src.length) { 44 builder.append(src, offset, src.length - offset); 45 } 46 } 47 return builder.toString(); 48 }

下面看下DynamicCheckerTokenParser怎么處理?

 1   private static class DynamicCheckerTokenParser implements TokenHandler {
 2 
 3     private boolean isDynamic;
 4 
 5     public DynamicCheckerTokenParser() {
 6       // Prevent Synthetic Access
 7     }
 8 
 9     public boolean isDynamic() {
10       return isDynamic;
11     }
12   //實現上什么也沒做,因為內部類就把父類的變量設置為true,告訴父類這個Node含有DynamicTag就好了。
13     @Override
14     public String handleToken(String content) {
15       this.isDynamic = true;
16       return null;
17     }
18   }

這樣就好了,如果有標簽SqlSource就是DynamicSqlSourcr() 沒有就是RawSqlSource():

這里在額外講一下,TextSqlNode還有一個內部類BindingTokenParser也實現了Tokenhandler接口看下他是怎么實現的,就提前知道了參數值是怎么具體獲取的。

 1   private static class BindingTokenParser implements TokenHandler {
 2 
 3     private DynamicContext context;
 4     private Pattern injectionFilter;
 5 
 6     public BindingTokenParser(DynamicContext context, Pattern injectionFilter) {
 7       this.context = context;
 8       this.injectionFilter = injectionFilter;
 9     }
10 
11     @Override
12     public String handleToken(String content) {
13       Object parameter = context.getBindings().get("_parameter");
14       if (parameter == null) {
15         context.getBindings().put("value", null);
16       } else if (SimpleTypeRegistry.isSimpleType(parameter.getClass())) {
17         context.getBindings().put("value", parameter);
18       }
//根據傳入的key到parameter里面找 這樣就把變量替換掉了。 后面講sql執行過程再詳細講解
19 Object value = OgnlCache.getValue(content, context.getBindings()); 20 String srtValue = (value == null ? "" : String.valueOf(value)); // issue #274 return "" instead of "null" 21 checkInjection(srtValue); 22 return srtValue; 23 } 24 25 private void checkInjection(String value) { 26 if (injectionFilter != null && !injectionFilter.matcher(value).matches()) { 27 throw new ScriptingException("Invalid input. Please conform to regex" + injectionFilter.pattern()); 28 } 29 } 30 }

 











免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM