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 }









原文地址:https://www.cnblogs.com/haoerlv/p/9930100.html