简介

在上篇文章中,我们介绍了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 #3if (script.startsWith("<script>")) {XPathParser parser = new XPathParser(script, false, configuration.getVariables(), new XMLMapperEntityResolver());return createSqlSource(configuration, parser.evalNode("/script"), parameterType);} else {// issue #127script = 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 tokenint 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和jdbcTypeprivate 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 paramspropertyType = 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获取MapMap<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) {// #591handler = pickSoleHandler(jdbcHandlerMap);}}// type drives generics herereturn (TypeHandler<T>) handler;}

总结

在本篇文章中,我们初步探索了Mapper初始化的一部分,着重解析了TypeHandler相关的注册和MapperProxy生成时TypeHandler对应的获取

核心流程如下:

  • 1.注册TypeHandler:放入Map中
  • 2.MapperProxy生成,字段的TypeHandler是在SQLSource中,生成SQLSource
  • 3.解析SQL语句,得到对应参数和返回字段的TypeHandler:这部分没细研究,但后面如果遇到问题,可以回来参考参考
  • 4.根据JavaType和jdbcType得到对应的TypeHandler

这部分还有很多的细节没来得及去细看,但如果遇到问题,还是能提供方向性的指导

MyBatis3源码解析(7)TypeHandler注册与获取相关推荐

  1. MyBatis3源码解析(6)TypeHandler使用

    简介 在上几篇中,介绍了MyBatis3对参数和结果的解析转换,对于常规数据类型,默认的处理已经足够应付了,但日常开发中会有一些特殊的类型,就可以通过TypeHandler来进行处理 示例准备 本篇文 ...

  2. mybatis源码解析(五) --- typehandler注册和处理的查询结果对象的类型转换

    上一次分析了mapper接口动态代理调用的处理过程,在查询结果后,然后转换的java所对应的类型,这篇文章将对这个类型转换处理详细介绍,在Configuration中TypeHandlerRegist ...

  3. Mybatis3 源码解析系列

    简介 Mybatis作为一个优秀的Java持久化框架,在我们的日常工作中相信都会用到,本次源码解析系列,就开始探索下Mybatis 总结 在MyBatis的学习中,首先通读了<MyBatis3源 ...

  4. MyBatis3源码解析(5)查询结果处理

    简介 上篇中解析了MyBatis3中参数是如何传递处理的,本篇接着看看在获取到查询结果后,MyBatis3是如何将SQL查询结果与我们接口函数定义的返回结果对应的 源码 获取结果后处理的入口 在:My ...

  5. 注册中心 Eureka 源码解析 —— 应用实例注册发现(五)之过期

    2019独角兽企业重金招聘Python工程师标准>>> 摘要: 原创出处 http://www.iocoder.cn/Eureka/instance-registry-evict/ ...

  6. MyBatis3源码解析(3)查询语句执行

    简介 上篇探索了MyBatis中如何获取数据库连接,本篇继续探索,来看看MyBatis中如何执行一条查询语句 测试代码 本篇文中用于调试的测试代码请参考:MyBatis3源码解析(1)探索准备 完整的 ...

  7. MyBatis3源码解析(2)数据库连接

    简介 基于上篇的示例感受,下面我们探索下MyBatis连接数据库的细节是如果实现的,在日常使用中是如何能和Druid数据库连接池等配合起来的 源码解析 基于上篇的示例代码: public class ...

  8. MyBatis3源码解析(1)探索准备

    简介 本篇文章将使用原生的JDBC方式操作数据库,然后在使用Mybatis提供的方式操作数据库,通过对比两部分的操作,大致得到Mybatis所做的主要工作,为接下来的源码解析做准备 示例代码 完整的工 ...

  9. MyBatis3源码解析(8)MyBatis与Spring的结合

    简介 在上几篇文章中,解析了MyBatis的核心原理部分,我们大致对其有了一定的了解,接下来我们看看在日常的开发中MyBatis是如何与Spring框架结合的 源码解析 在我们的日常开发中,使用Spr ...

最新文章

  1. seaborn系列(1) | 关系类图relplot用法
  2. 用html做简易计步器,HTML5 运动计步器
  3. DateTimePicker1.DateTime:=IncMonth(Trunc(now+1)-1/24/60/60,1);
  4. java--内存模型
  5. python项目需求文档模板_Python+docxtpl+plotly实现模板word文档图表生成
  6. LaTeX 语法教程
  7. java实现一个录像大师
  8. 元器件保护必备知识——静电防护
  9. python爬楼梯多少种_LeetCode70爬楼梯-Python3-两种方法实现
  10. sql注入搞事情(连载一)
  11. oa处理会签流程图_OA-审批流程管理模块
  12. React中setState() 函数的三种用法
  13. GreenPlum数据库卸数、装数
  14. 实现ppt幻灯片播放倒计时
  15. 设置本地yum源,下载vim
  16. python关闭浏览器、未过期的session_解决因为关闭浏览器造成session失效的假象
  17. 如何用Excel画出漂亮的图(office 2016)
  18. SAND-MATIC 质押池将停用
  19. Unity UGUI NGUI 模型 粒子特效 三者之间 渲染层级设置
  20. PyQt5高级界面控件之QDockWidget

热门文章

  1. 【转】Android android listview的HeadView左右切换图片(仿新浪,网易,百度等切换图片)...
  2. java异常中的Error和Exception的区别是什么?
  3. MicrosoftFixit50688 [Windows7事件ID10,WMI错误的解决方法
  4. 解决苹果手机点击返回键页面不刷新问题
  5. 纯CSS的方法解决文字溢出与截断的问题
  6. KL散度的通俗易懂理解
  7. Python入门到精通三天速成第三讲——多重继承
  8. 【采访】腾讯社交广告高校算法大赛决赛第一周最大进步队伍——SkullGreymon比赛经验及心得分享
  9. 谷歌 ICLR 2020 | 向量化召回也需要『预训练』
  10. python实现屏幕录制_GitHub - Sijiu/record-camera-and-screen: 录制摄像头和录制屏幕,两者之间可以轻易切换...