MyBatis3 源码解析 (7)TypeHandler 注册与获取
- 2022 年 2 月 16 日
本文字数:5146 字
阅读完需:约 17 分钟
简介
在上篇文章中,我们介绍了 TypeHandler 的简单使用和解析了 TypeHandler 的处理核心,这篇文章中我们接着看到 TypeHandler 是如注册和获取使用的
源码解析
TypeHandler 注册
typeHandler 注册的函数代码如下:
根据 JavaType 放入第一层 Map
根据 jdbcType 放入第二层 Map
configuration.getTypeHandlerRegistry().register(String[].class, JdbcType.VARCHAR, StringArrayTypeHandler.class);
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) { if (javaType != null) { Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType); if (map == null || map == NULL_TYPE_HANDLER_MAP) { map = new HashMap<>(); } map.put(jdbcType, handler); typeHandlerMap.put(javaType, map); } allTypeHandlersMap.put(handler.getClass(), handler); }
TypeHandler 获取
在探索中,我们发现是在 Mapper 初始化的过程就会去获取 TypeHandler 了,在示例代码中我们调用了函数:addMapper
在该函数中,我们看到了我们熟悉的(前面文章分析过,Sql 执行其实就是从 Mapper 代理类开始的)MapperProxy 相关的东西
public <T> void addMapper(Class<T> type) { if (type.isInterface()) { if (hasMapper(type)) { throw new BindingException("Type " + type + " is already known to the MapperRegistry."); } boolean loadCompleted = false; try { // 生成我们熟悉的Mapper代理类 knownMappers.put(type, new MapperProxyFactory<>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); // 进行解析 parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }
跟踪下面,我们看到 SQLSource 生成的相关代码,ParameterMapper 就是从 SQLSource 带下去的
void parseStatement(Method method) { ...... getAnnotationWrapper(method, true, statementAnnotationTypes).ifPresent(statementAnnotation -> { final SqlSource sqlSource = buildSqlSource(statementAnnotation.getAnnotation(), parameterTypeClass, languageDriver, method); ...... }); }
public SqlSource createSqlSource(Configuration configuration, String script, Class<?> parameterType) { // issue #3 if (script.startsWith("<script>")) { XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver()); return createSqlSource(configuration, parser.evalNode("/script"), parameterType); } else { // issue #127 script = PropertyParser.parse(script, configuration.getVariables()); TextSqlNode textSqlNode = new TextSqlNode(script); if (textSqlNode.isDynamic()) { // 这个暂时不理解 return new DynamicSqlSource(configuration, textSqlNode); } else { // 目前走的这个 return new RawSqlSource(configuration, script, parameterType); } } }
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; if (configuration.isShrinkWhitespacesInSql()) { sql = parser.parse(removeExtraWhitespaces(originalSql)); } else { // 走的这个 sql = parser.parse(originalSql); } return new StaticSqlSource(configuration, sql, handler.getParameterMappings()); }
下面是对 SQL 语句进行了解析,最终的效果是遍历了我们示例 SQL 中的 id 和 name,对其找到对应的 TypeHandler
细节部分没怎么看到,暂时跳过
public class GenericTokenParser { public String parse(String text) { if (text == null || text.isEmpty()) { return ""; } // search open token int start = text.indexOf(openToken); if (start == -1) { return text; } char[] src = text.toCharArray(); int offset = 0; final StringBuilder builder = new StringBuilder(); StringBuilder expression = null; do { if (start > 0 && src[start - 1] == '\\') { // this open token is escaped. remove the backslash and continue. builder.append(src, offset, start - offset - 1).append(openToken); offset = start + openToken.length(); } else { // found open token. let's search close token. if (expression == null) { expression = new StringBuilder(); } else { expression.setLength(0); } builder.append(src, offset, start - offset); offset = start + openToken.length(); int end = text.indexOf(closeToken, offset); while (end > -1) { if (end > offset && src[end - 1] == '\\') { // this close token is escaped. remove the backslash and continue. expression.append(src, offset, end - offset - 1).append(closeToken); offset = end + closeToken.length(); end = text.indexOf(closeToken, offset); } else { expression.append(src, offset, end - offset); break; } } if (end == -1) { // close token was not found. builder.append(src, start, src.length - start); offset = src.length; } else { builder.append(handler.handleToken(expression.toString())); offset = end + closeToken.length(); } } start = text.indexOf(openToken, offset); } while (start > -1); if (offset < src.length) { builder.append(src, offset, src.length - offset); } return builder.toString(); }}
下面就是根据对应的 JavaType 和 jdbcType,获取对应的 TypeHandler
public String handleToken(String content) { parameterMappings.add(buildParameterMapping(content)); return "?"; }
// 在其中设置JavaType和jdbcType 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 " + PARAMETER_PROPERTIES); } } if (typeHandlerAlias != null) { builder.typeHandler(resolveTypeHandler(javaType, typeHandlerAlias)); } return builder.build(); }
public ParameterMapping build() { resolveTypeHandler(); validate(); return parameterMapping; }
private void resolveTypeHandler() { if (parameterMapping.typeHandler == null && parameterMapping.javaType != null) { Configuration configuration = parameterMapping.configuration; TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry(); // 从Map中获取对应的TypeHandler,以javaType和jdbcType为标识 parameterMapping.typeHandler = typeHandlerRegistry.getTypeHandler(parameterMapping.javaType, parameterMapping.jdbcType); } }
private <T> TypeHandler<T> getTypeHandler(Type type, JdbcType jdbcType) { if (ParamMap.class.equals(type)) { return null; } // 根据javaType获取Map Map<JdbcType, TypeHandler<?>> jdbcHandlerMap = getJdbcHandlerMap(type); TypeHandler<?> handler = null; if (jdbcHandlerMap != null) { // 根据jdbcType获取 handler = jdbcHandlerMap.get(jdbcType); if (handler == null) { handler = jdbcHandlerMap.get(null); } if (handler == null) { // #591 handler = pickSoleHandler(jdbcHandlerMap); } } // type drives generics here return (TypeHandler<T>) handler; }
总结
在本篇文章中,我们初步探索了 Mapper 初始化的一部分,着重解析了 TypeHandler 相关的注册和 MapperProxy 生成时 TypeHandler 对应的获取
核心流程如下:
1.注册 TypeHandler:放入 Map 中
2.MapperProxy 生成,字段的 TypeHandler 是在 SQLSource 中,生成 SQLSource
3.解析 SQL 语句,得到对应参数和返回字段的 TypeHandler:这部分没细研究,但后面如果遇到问题,可以回来参考参考
4.根据 JavaType 和 jdbcType 得到对应的 TypeHandler
这部分还有很多的细节没来得及去细看,但如果遇到问题,还是能提供方向性的指导
版权声明: 本文为 InfoQ 作者【萧】的原创文章。
原文链接:【http://xie.infoq.cn/article/86f72a54a8de0d94b299defab】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
萧
还未添加个人签名 2018.09.09 加入
代码是门手艺活,也是门艺术活











评论