MyBatis启动之XMLConfigBuilder解析配置文件(二)

源码共读

共 18210字,需浏览 37分钟

 ·

2021-02-02 04:02

Python实战社群

Java实战社群

长按识别下方二维码,按需求添加

扫码关注添加客服

进Python社群▲

扫码关注添加客服

进Java社群


作者丨ytao

来源丨ytao

前言

XMLConfigBuilderBaseBuilder(解析中会涉及到讲解)的其中一个子类,它的作用是把MyBatis的XML及相关配置解析出来,然后保存到 Configuration中。本文就解析过程按照执行顺序进行分析,掌握常用配置的解析原理。

使用

调用 XMLConfigBuilder进行解析,要进行两步操作,上篇文章中【MyBatis之启动分析(一)】有提到。

实例化 XMLConfigBuilder对象。

  1. privateXMLConfigBuilder(XPathParser parser, String environment, Properties props) {

  2. // 调用父类的构造方法

  3. super(newConfiguration());

  4. ErrorContext.instance().resource("SQL Mapper Configuration");

  5. this.configuration.setVariables(props);

  6. this.parsed = false;

  7. this.environment = environment;

  8. this.parser = parser;

  9. }

实例化 Configuration

通过 newConfiguration()的方式实例化:typeAliasRegistry是一个类型别名注册器,实现原理就是维护一份 HashMap,别名作为 key,类的全限定名作为 value。这里将框架中使用的类注册到类型别名注册器中。TypeAliasRegistry.registerAlias代码如下:

  1. publicvoid registerAlias(String alias, Class value) {

  2. if(alias == null) {

  3. thrownewTypeException("The parameter alias cannot be null");

  4. }

  5. // issue #748

  6. // 在验证是否存在key和保存kv前,统一将key转换成小写

  7. String key = alias.toLowerCase(Locale.ENGLISH);

  8. if(TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null&& !TYPE_ALIASES.get(key).equals(value)) {

  9. // 当注册的类型已存在时,抛出异常

  10. thrownewTypeException("The alias '"+ alias + "' is already mapped to the value '"+ TYPE_ALIASES.get(key).getName() + "'.");

  11. }

  12. // TYPE_ALIASES 为定义的一个HashMap

  13. TYPE_ALIASES.put(key, value);

  14. }

在实例化 Configuration类过程中,在该类里除了实例化了 TypeAliasRegistry还实例化了另外一个下面用到的的类:

  1. // 类型处理器注册器

  2. protectedfinalTypeAliasRegistry typeAliasRegistry = newTypeAliasRegistry();

TypeHandlerRegistryTypeAliasRegistry实例化逻辑相似,里面注册了一些常用类型和处理器,代码易懂。TypeHandlerRegistry的属性

  1. // jdbc类型和TypeHandler的映射关系,key必须是JdbcType的枚举类型,读取结果集数据时,将jdbc类型转换成java类型

  2. privatefinalMap<JdbcType, TypeHandler> JDBC_TYPE_HANDLER_MAP = newEnumMap<JdbcType, TypeHandler>(JdbcType.class);

  3. // Java类型与JdbcType类型的键值对,存在一对多的映射关系

  4. privatefinalMap<Type, Map<JdbcType, TypeHandler>> TYPE_HANDLER_MAP = newConcurrentHashMap<Type, Map<JdbcType, TypeHandler>>();

  5. // 没有相应的类型处理器时,使用的处理器

  6. privatefinalTypeHandler<Object> UNKNOWN_TYPE_HANDLER = newUnknownTypeHandler(this);

  7. // 类型处理器类类型和类型处理器的映射关系

  8. privatefinalMap<Class, TypeHandler> ALL_TYPE_HANDLERS_MAP = newHashMap<Class, TypeHandler>();

  9. // 空处理器的值,用来做校验

  10. privatestaticfinalMap<JdbcType, TypeHandler> NULL_TYPE_HANDLER_MAP = Collections.emptyMap();

  11. // 默认枚举类型处理器

  12. privateClassextendsTypeHandler> defaultEnumTypeHandler = EnumTypeHandler.class;

TypeHandlerRegistry构造函数:

  1. publicTypeHandlerRegistry() {

  2. register(Boolean.class, newBooleanTypeHandler());

  3. register(boolean.class, newBooleanTypeHandler());

  4. register(JdbcType.BOOLEAN, newBooleanTypeHandler());

  5. register(JdbcType.BIT, newBooleanTypeHandler());


  6. register(Byte.class, newByteTypeHandler());

  7. register(byte.class, newByteTypeHandler());

  8. register(JdbcType.TINYINT, newByteTypeHandler());


  9. register(Short.class, newShortTypeHandler());

  10. register(short.class, newShortTypeHandler());

  11. register(JdbcType.SMALLINT, newShortTypeHandler());


  12. register(Integer.class, newIntegerTypeHandler());

  13. register(int.class, newIntegerTypeHandler());

  14. register(JdbcType.INTEGER, newIntegerTypeHandler());


  15. register(Long.class, newLongTypeHandler());

  16. register(long.class, newLongTypeHandler());


  17. register(Float.class, newFloatTypeHandler());

  18. register(float.class, newFloatTypeHandler());

  19. register(JdbcType.FLOAT, newFloatTypeHandler());


  20. register(Double.class, newDoubleTypeHandler());

  21. register(double.class, newDoubleTypeHandler());

  22. register(JdbcType.DOUBLE, newDoubleTypeHandler());


  23. register(Reader.class, newClobReaderTypeHandler());

  24. register(String.class, newStringTypeHandler());

  25. register(String.class, JdbcType.CHAR, newStringTypeHandler());

  26. register(String.class, JdbcType.CLOB, newClobTypeHandler());

  27. register(String.class, JdbcType.VARCHAR, newStringTypeHandler());

  28. register(String.class, JdbcType.LONGVARCHAR, newClobTypeHandler());

  29. register(String.class, JdbcType.NVARCHAR, newNStringTypeHandler());

  30. register(String.class, JdbcType.NCHAR, newNStringTypeHandler());

  31. register(String.class, JdbcType.NCLOB, newNClobTypeHandler());

  32. register(JdbcType.CHAR, newStringTypeHandler());

  33. register(JdbcType.VARCHAR, newStringTypeHandler());

  34. register(JdbcType.CLOB, newClobTypeHandler());

  35. register(JdbcType.LONGVARCHAR, newClobTypeHandler());

  36. register(JdbcType.NVARCHAR, newNStringTypeHandler());

  37. register(JdbcType.NCHAR, newNStringTypeHandler());

  38. register(JdbcType.NCLOB, newNClobTypeHandler());


  39. register(Object.class, JdbcType.ARRAY, newArrayTypeHandler());

  40. register(JdbcType.ARRAY, newArrayTypeHandler());


  41. register(BigInteger.class, newBigIntegerTypeHandler());

  42. register(JdbcType.BIGINT, newLongTypeHandler());


  43. register(BigDecimal.class, newBigDecimalTypeHandler());

  44. register(JdbcType.REAL, newBigDecimalTypeHandler());

  45. register(JdbcType.DECIMAL, newBigDecimalTypeHandler());

  46. register(JdbcType.NUMERIC, newBigDecimalTypeHandler());


  47. register(InputStream.class, newBlobInputStreamTypeHandler());

  48. register(Byte[].class, newByteObjectArrayTypeHandler());

  49. register(Byte[].class, JdbcType.BLOB, newBlobByteObjectArrayTypeHandler());

  50. register(Byte[].class, JdbcType.LONGVARBINARY, newBlobByteObjectArrayTypeHandler());

  51. register(byte[].class, newByteArrayTypeHandler());

  52. register(byte[].class, JdbcType.BLOB, newBlobTypeHandler());

  53. register(byte[].class, JdbcType.LONGVARBINARY, newBlobTypeHandler());

  54. register(JdbcType.LONGVARBINARY, newBlobTypeHandler());

  55. register(JdbcType.BLOB, newBlobTypeHandler());


  56. register(Object.class, UNKNOWN_TYPE_HANDLER);

  57. register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);

  58. register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);


  59. register(Date.class, newDateTypeHandler());

  60. register(Date.class, JdbcType.DATE, newDateOnlyTypeHandler());

  61. register(Date.class, JdbcType.TIME, newTimeOnlyTypeHandler());

  62. register(JdbcType.TIMESTAMP, newDateTypeHandler());

  63. register(JdbcType.DATE, newDateOnlyTypeHandler());

  64. register(JdbcType.TIME, newTimeOnlyTypeHandler());


  65. register(java.sql.Date.class, newSqlDateTypeHandler());

  66. register(java.sql.Time.class, newSqlTimeTypeHandler());

  67. register(java.sql.Timestamp.class, newSqlTimestampTypeHandler());


  68. // mybatis-typehandlers-jsr310

  69. // 是否包含日期,时间相关的Api,通过判断是否加载java.time.Clock作为依据

  70. if(Jdk.dateAndTimeApiExists) {

  71. this.register(Instant.class, InstantTypeHandler.class);

  72. this.register(LocalDateTime.class, LocalDateTimeTypeHandler.class);

  73. this.register(LocalDate.class, LocalDateTypeHandler.class);

  74. this.register(LocalTime.class, LocalTimeTypeHandler.class);

  75. this.register(OffsetDateTime.class, OffsetDateTimeTypeHandler.class);

  76. this.register(OffsetTime.class, OffsetTimeTypeHandler.class);

  77. this.register(ZonedDateTime.class, ZonedDateTimeTypeHandler.class);

  78. this.register(Month.class, MonthTypeHandler.class);

  79. this.register(Year.class, YearTypeHandler.class);

  80. this.register(YearMonth.class, YearMonthTypeHandler.class);

  81. this.register(JapaneseDate.class, JapaneseDateTypeHandler.class);

  82. }


  83. // issue #273

  84. register(Character.class, newCharacterTypeHandler());

  85. register(char.class, newCharacterTypeHandler());

  86. }

里面调用了两个 register()重载方法, type+handler 参的 TypeHandlerRegistry.register(ClassjavaType,TypeHandlertypeHandler)type+jdbc type+handler 参的 TypeHandlerRegistry.register(Classtype,JdbcTypejdbcType,TypeHandlerhandler)

  1. // java type + handler

  2. public voidregister(Class javaType, TypeHandlerextends T> typeHandler) {

  3. register((Type) javaType, typeHandler);

  4. }


  5. private voidregister(Type javaType, TypeHandlerextends T> typeHandler) {

  6. MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);

  7. if(mappedJdbcTypes != null) {

  8. for(JdbcType handledJdbcType : mappedJdbcTypes.value()) {

  9. register(javaType, handledJdbcType, typeHandler);

  10. }

  11. if(mappedJdbcTypes.includeNullJdbcType()) {

  12. register(javaType, null, typeHandler);

  13. }

  14. } else{

  15. register(javaType, null, typeHandler);

  16. }

  17. }


  18. // java type + jdbc type + handler

  19. public voidregister(Class type, JdbcType jdbcType, TypeHandlerextends T> handler) {

  20. register((Type) type, jdbcType, handler);

  21. }


  22. // type + handler 和 type + jdbc type + handler 最终都调用此方法

  23. privatevoidregister(Type javaType, JdbcType jdbcType, TypeHandler handler) {

  24. if(javaType != null) {

  25. // 当 javaType 不为空时, 获取 java 类型的的映射

  26. Map<JdbcType, TypeHandler> map = TYPE_HANDLER_MAP.get(javaType);

  27. if(map == null|| map == NULL_TYPE_HANDLER_MAP) {

  28. // 若映射为空,新建一个映射关系

  29. map = newHashMap<JdbcType, TypeHandler>();

  30. // 保存至类型处理器映射关系中

  31. TYPE_HANDLER_MAP.put(javaType, map);

  32. }

  33. // 保存jdbcType和处理器关系,完成 java类型,jdbc类型,处理器三者之间的注册

  34. map.put(jdbcType, handler);

  35. }

  36. // 保存处理器信息中

  37. ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);

  38. }


  39.    // MappedJdbcTypes 注解

  40. @Documented

  41. @Retention(RetentionPolicy.RUNTIME)

  42. @Target(ElementType.TYPE)

  43. public@interfaceMappedJdbcTypes{

  44. JdbcType[] value();

  45. boolean includeNullJdbcType() defaultfalse;

  46. }

  • type+handler方法:先获取处理器的 MappedJdbcTypes注解(自定义处理器注解),若注解的 value值不为空时,由于该值为 JdbcType[]类型,所以 for循环 javaType+jdbcType+TypeHandler注册,若 includeNullJdbcType( jdbcType是否包含 null)为 true,默认值为 false,注册到相应映射中。若注解的 value为 null,直接调用注册操作,里面不会注册 type+jdbc type+handler关系。

  • type+jdbc type+handler方法:该方法将java类强制转换为 java.lang.reflect.Type类型,然后调用最终注册的方法。

调用父类 BaseBuilder的构造方法

BaseBuilder定义有三个属性

  1. protectedfinalConfiguration configuration;

  2. // 类型别名注册器

  3. protectedfinalTypeAliasRegistry typeAliasRegistry;

  4. // 类型处理器注册器

  5. protectedfinalTypeHandlerRegistry typeHandlerRegistry;

BaseBuilder构造方法

  1. publicBaseBuilder(Configuration configuration) {

  2. this.configuration = configuration;

  3. this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();

  4. this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();

  5. }

这里属性,就是上面讲解到的。

调用 XMLConfigBuilder.parse() 作为解析入口。

parse()实现配置文件是否解析过

  1. publicConfiguration parse() {

  2. // 若parsed为true,配置文件解析过

  3. if(parsed) {

  4. thrownewBuilderException("Each XMLConfigBuilder can only be used once.");

  5. }

  6. // 标志已解析过

  7. parsed = true;

  8. // 从根节点 configuration 开始解析

  9. parseConfiguration(parser.evalNode("/configuration"));

  10. return configuration;

  11. }

解析 /configuration里的配置

  1. privatevoid parseConfiguration(XNode root) {

  2. try{

  3. //issue #117 read properties first

  4. propertiesElement(root.evalNode("properties"));

  5. Properties settings = settingsAsProperties(root.evalNode("settings"));

  6. loadCustomVfs(settings);

  7. typeAliasesElement(root.evalNode("typeAliases"));

  8. pluginElement(root.evalNode("plugins"));

  9. objectFactoryElement(root.evalNode("objectFactory"));

  10. objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));

  11. reflectorFactoryElement(root.evalNode("reflectorFactory"));

  12. settingsElement(settings);

  13. // read it after objectFactory and objectWrapperFactory issue #631

  14. environmentsElement(root.evalNode("environments"));

  15. databaseIdProviderElement(root.evalNode("databaseIdProvider"));

  16. typeHandlerElement(root.evalNode("typeHandlers"));

  17. mapperElement(root.evalNode("mappers"));

  18. } catch(Exception e) {

  19. thrownewBuilderException("Error parsing SQL Mapper Configuration. Cause: "+ e, e);

  20. }

  21. }

从上面源码中,不难看出这里是解析 /configuration中的各个子节点。

properties 节点解析

properties配置方式
  1. "username" value="${jdbc.username}"/>


  2. "xxxConfig.properties">


  3. "file:///D:/xxxConfig.properties">

propertiesElement()方法
  1. privatevoid propertiesElement(XNode context) throwsException{

  2. if(context != null) {

  3. // 获取 propertie 节点,并保存 Properties 中

  4. Properties defaults = context.getChildrenAsProperties();

  5. // 获取 resource 的值

  6. String resource = context.getStringAttribute("resource");

  7. // 获取 url 的值

  8. String url = context.getStringAttribute("url");

  9. if(resource != null&& url != null) {

  10. thrownewBuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");

  11. }

  12. if(resource != null) {

  13. defaults.putAll(Resources.getResourceAsProperties(resource));

  14. } elseif(url != null) {

  15. defaults.putAll(Resources.getUrlAsProperties(url));

  16. }

  17. Properties vars = configuration.getVariables();

  18. if(vars != null) {

  19. defaults.putAll(vars);

  20. }

  21. // 将解析的值保存到 XPathParser 中

  22. parser.setVariables(defaults);

  23. // 将解析的值保存到 Configuration 中

  24. configuration.setVariables(defaults);

  25. }

  26. }

从上面源码中, resourceurl的配置形式不允许同时存在,否则抛出 BuilderException异常。先解析 propertie的配置值,再解析 resourceurl的值。当 propertie存在与 resourceurl相同的 key时, propertie的配置会被覆盖,应为 Properties实现的原理就是继承的 Hashtable类来实现的。

settings 节点解析

settings配置方式
  1. "cacheEnabled" value="true"/>

  2. ......

设置中各项的意图、默认值(引用来源:w3cschool)

设置参数描述有效值默认值
cacheEnabled该配置影响的所有映射器中配置的缓存的全局开关。true,falsetrue
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。true,falsefalse
aggressiveLazyLoading当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载。true,false
multipleResultSetsEnabled是否允许单一语句返回多结果集(需要兼容驱动)。true,falsetrue
useColumnLabel使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。true,falsetrue
useGeneratedKeys允许 JDBC 支持自动生成主键,需要驱动兼容。如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。true,falseFalse
autoMappingBehavior指定 MyBatis 应如何自动映射列到字段或属性。NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。FULL 会自动映射任意复杂的结果集(无论是否嵌套)。NONE, PARTIAL, FULLPARTIAL
defaultExecutorType配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements);BATCH 执行器将重用语句并执行批量更新。SIMPLE REUSE BATCHSIMPLE
defaultStatementTimeout设置超时时间,它决定驱动等待数据库响应的秒数。Any positive integerNot Set (null)
safeRowBoundsEnabled允许在嵌套语句中使用分页(RowBounds)。true,falseFalse
mapUnderscoreToCamelCase是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。true, falseFalse
localCacheScopeMyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。SESSION,STATEMENTSESSION
jdbcTypeForNull当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。JdbcType enumeration. Most common are: NULL, VARCHAR and OTHEROTHER
lazyLoadTriggerMethods指定哪个对象的方法触发一次延迟加载。A method name list separated by commasequals,clone,hashCode,toString
defaultScriptingLanguage指定动态 SQL 生成的默认语言。A type alias or fully qualified class name.org.apache.ibatis.scripting.xmltags.XMLDynamicLanguageDriver
callSettersOnNulls指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null 的。true,falsefalse
logPrefix指定 MyBatis 增加到日志名称的前缀。Any StringNot set
logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J, LOG4J, LOG4J2, JDKLOGGING, COMMONSLOGGING, STDOUTLOGGING, NOLOGGINGNot set
proxyFactory指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。CGLIB JAVASSISTCGLIB
settingsAsProperties()方法
  1. privateProperties settingsAsProperties(XNode context) {

  2. if(context == null) {

  3. returnnewProperties();

  4. }

  5. // 获取setting节点的name和value,并保存至Properties返回

  6. Properties props = context.getChildrenAsProperties();

  7. // Check that all settings are known to the configuration class

  8. // 创建Configuration的MetaClass

  9. MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);

  10. // 校验Configuration中是否有setting设置的name值

  11. for(Object key : props.keySet()) {

  12. if(!metaConfig.hasSetter(String.valueOf(key))) {

  13. thrownewBuilderException("The setting "+ key + " is not known. Make sure you spelled it correctly (case sensitive).");

  14. }

  15. }

  16. return props;

  17. }

这里获取到 setting的值,并返回 Properties对象。然后做配置的 name是否合法。org.apache.ibatis.reflection.MetaClass类是保存着一个利用反射获取到的类信息, metaConfig.hasSetter(String.valueOf(key))是判断 metaConfig对象中是否包含 key属性。

vfsImpl()方法
  1. privatevoid loadCustomVfs(Properties props) throwsClassNotFoundException{

  2. String value = props.getProperty("vfsImpl");

  3. if(value != null) {

  4. String[] clazzes = value.split(",");

  5. for(String clazz : clazzes) {

  6. if(!clazz.isEmpty()) {

  7. @SuppressWarnings("unchecked")

  8. Classextends VFS> vfsImpl = (Classextends VFS>)Resources.classForName(clazz);

  9. configuration.setVfsImpl(vfsImpl);

  10. }

  11. }

  12. }

  13. }

该方法是解析虚拟文件系统配置,用来加载自定义虚拟文件系统的资源。类保存在 Configuration.vfsImpl中。

settingsElement()方法

这个方法的作用就是将解析的 settings设置到 configuration

  1. privatevoid settingsElement(Properties props) throwsException{

  2. configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));

  3. configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));

  4. configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));

  5. configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));

  6. configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));

  7. configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), false));

  8. configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));

  9. configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));

  10. configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));

  11. configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));

  12. configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));

  13. configuration.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));

  14. configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));

  15. configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));

  16. configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));

  17. configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));

  18. configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));

  19. configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));

  20. configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));

  21. @SuppressWarnings("unchecked")

  22. ClassextendsTypeHandler> typeHandler = (ClassextendsTypeHandler>)resolveClass(props.getProperty("defaultEnumTypeHandler"));

  23. configuration.setDefaultEnumTypeHandler(typeHandler);

  24. configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));

  25. configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));

  26. configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));

  27. configuration.setLogPrefix(props.getProperty("logPrefix"));

  28. @SuppressWarnings("unchecked")

  29. ClassextendsLog> logImpl = (ClassextendsLog>)resolveClass(props.getProperty("logImpl"));

  30. configuration.setLogImpl(logImpl);

  31. configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));

  32. }

typeAliases 节点解析

typeAliases配置方式
  1. <package name="com.ytao.main.model"/>

  2. // 或

  3. "com.ytao.main.model.Student" alias="student"/>

  4. "com.ytao.main.model.Person"/>

该节点是配置类和别名的关系

  1. package节点是配置整个包下的类

  2. typeAlias节点是指定配置单个类, type为必填值且为类全限定名, alias为选填。配置后,是该类时,可直接使用别名。

typeAliasesElement()方法
  1. privatevoid typeAliasesElement(XNode parent) {

  2. if(parent != null) {

  3. for(XNode child : parent.getChildren()) {

  4. if("package".equals(child.getName())) {

  5. // 以 package 方式配置

  6. String typeAliasPackage = child.getStringAttribute("name");

  7. configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);

  8. } else{

  9. // 以 alias 方式配置

  10. String alias = child.getStringAttribute("alias");

  11. String type = child.getStringAttribute("type");

  12. try{

  13. Class clazz = Resources.classForName(type);

  14. if(alias == null) {

  15. typeAliasRegistry.registerAlias(clazz);

  16. } else{

  17. typeAliasRegistry.registerAlias(alias, clazz);

  18. }

  19. } catch(ClassNotFoundException e) {

  20. thrownewBuilderException("Error registering typeAlias for '"+ alias + "'. Cause: "+ e, e);

  21. }

  22. }

  23. }

  24. }

  25. }

使用 package 配置

当扫描 package时,获取到包名后 TypeAliasRegistry.registerAliases(typeAliasPackage)

  1. publicvoid registerAliases(String packageName){

  2. registerAliases(packageName, Object.class);

  3. }


  4. publicvoid registerAliases(String packageName, Class superType){

  5. ResolverUtil<Class> resolverUtil = newResolverUtil<Class>();

  6. // 获取 package 下所有已 .class 结尾的文件

  7. resolverUtil.find(newResolverUtil.IsA(superType), packageName);

  8. // 获取扫描出来的类

  9. Set<ClassextendsClass>> typeSet = resolverUtil.getClasses();

  10. for(Class type : typeSet){

  11. // Ignore inner classes and interfaces (including package-info.java)

  12. // Skip also inner classes. See issue #6

  13. // 过滤类

  14. if(!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {

  15. registerAlias(type);

  16. }

  17. }

  18. }

扫描到指定 package下所有以 .class结尾文件的类,并保存至Set集合中,然后遍历集合,过滤掉没有名称,接口,和底层特定类。最后 TypeAliasRegistry.registerAlias(Classtype)注册到别名注册器中。

  1. publicvoid registerAlias(Class type) {

  2. // 使用类的 simpleName 作为别名,也就是默认的别名命名规则

  3. String alias = type.getSimpleName();

  4. Alias aliasAnnotation = type.getAnnotation(Alias.class);

  5. if(aliasAnnotation != null) {

  6. alias = aliasAnnotation.value();

  7. }

  8. // 上面分析的最终注册的方法

  9. registerAlias(alias, type);

  10. }

通过类注册到注册器中时,如果该注册类有使用 @Aliasorg.apache.ibatis.type.Alias)注解,那么XML配置中配置的别名会被注解配置覆盖。

使用 typeAlias 配置

如果 typeAliasalias有设置值,使用自定名称方式注册,否则使用默认方式注册,即类的simpleName作为别名。

plugins 节点解析

plugins配置方式
  1. // 配置自定义插件,可指定在某个点进行拦截

  2. "com.ytao.main.plugin.DemoInterceptor">

  3. // 当前插件属性

  4. "name" value="100"/>

自定义插件需要实现 org.apache.ibatis.plugin.Interceptor接口,同时在注解上指定拦截的方法。

pluginElement()方法
  1. privatevoid pluginElement(XNode parent) throwsException{

  2. if(parent != null) {

  3. for(XNode child : parent.getChildren()) {

  4. // 获取自定插件的类名

  5. String interceptor = child.getStringAttribute("interceptor");

  6. // 获取插件属性

  7. Properties properties = child.getChildrenAsProperties();

  8. // 实例化 Interceptor

  9. Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();

  10. // 设置插件属性到插件中

  11. interceptorInstance.setProperties(properties);

  12. // 将插件保存在 configuration 中

  13. configuration.addInterceptor(interceptorInstance);

  14. }

  15. }

  16. }

这里取 节点的 interceptor可以使用别名设置。从源码中 resolveClass方法

  1. //

  2. protectedClass resolveClass(String alias) {

  3. if(alias == null) {

  4. returnnull;

  5. }

  6. try{

  7. return resolveAlias(alias);

  8. } catch(Exception e) {

  9. thrownewBuilderException("Error resolving class. Cause: "+ e, e);

  10. }

  11. }


  12. //

  13. protectedClass resolveAlias(String alias) {

  14. return typeAliasRegistry.resolveAlias(alias);

  15. }


  16. //

  17. public Class resolveAlias(String string) {

  18. try{

  19. if(string == null) {

  20. returnnull;

  21. }

  22. // issue #748

  23. // 将传入的 类 名称统一转换

  24. String key = string.toLowerCase(Locale.ENGLISH);

  25. Class value;

  26. // 验证别名中是否有当前传入的key

  27. if(TYPE_ALIASES.containsKey(key)) {

  28. value = (Class) TYPE_ALIASES.get(key);

  29. } else{

  30. value = (Class) Resources.classForName(string);

  31. }

  32. return value;

  33. } catch(ClassNotFoundException e) {

  34. thrownewTypeException("Could not resolve type alias '"+ string + "'. Cause: "+ e, e);

  35. }

  36. }

以上源码为别名解析过程,其他别名的解析也是调用此方法,先去保存的别名中去找,是否有别名,如果没有就通过 Resources.classForName生成实例。

objectFactory,objectWrapperFactory,reflectorFactory 节点解析

以上都是对实现类都是对MyBatis进行扩展。解析方法也类似,最后都是保存在 configuration

  1. // objectFactory 解析

  2. privatevoid objectFactoryElement(XNode context) throwsException{

  3. if(context != null) {

  4. String type = context.getStringAttribute("type");

  5. Properties properties = context.getChildrenAsProperties();

  6. ObjectFactory factory = (ObjectFactory) resolveClass(type).newInstance();

  7. factory.setProperties(properties);

  8. configuration.setObjectFactory(factory);

  9. }

  10. }


  11. // objectWrapperFactory 解析

  12. privatevoid objectWrapperFactoryElement(XNode context) throwsException{

  13. if(context != null) {

  14. String type = context.getStringAttribute("type");

  15. ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).newInstance();

  16. configuration.setObjectWrapperFactory(factory);

  17. }

  18. }


  19. // reflectorFactory 解析

  20. privatevoid reflectorFactoryElement(XNode context) throwsException{

  21. if(context != null) {

  22. String type = context.getStringAttribute("type");

  23. ReflectorFactory factory = (ReflectorFactory) resolveClass(type).newInstance();

  24. configuration.setReflectorFactory(factory);

  25. }

  26. }

以上为解析 objectFactory,objectWrapperFactory,reflectorFactory源码,经过前面的分析后,这里比较容易看懂。

environments 节点解析

environments配置方式
  1. default="development">

  2. "development">

  3. "JDBC">

  4. "prop" value="100"/>

  5. "UNPOOLED">

  6. "driver" value="com.mysql.jdbc.Driver"/>

  7. "url" value="${jdbc.url}"/>

  8. "username" value="${jdbc.username}"/>

  9. "password" value="${jdbc.password}"/>

  10. ......

该节点可设置多个环境,针对不同的环境单独配置。environments的属性 default是默认环境,该值对应一个 environment的属性 id的值。

  • transactionManager为事务管理,属性 type为事务管理类型,上面的介绍的 newConfiguration()有定义类型有:JDBC 和 MANAGED事务管理类型。

  • dataSource是数据源, type为数据源类型,与 transactionManager同理,可知内建的数据源类型有:JNDI,POOLED,UNPOOLED数据源类型。

environmentsElement()方法
  1. privatevoid environmentsElement(XNode context) throwsException{

  2. if(context != null) {

  3. if(environment == null) {

  4. environment = context.getStringAttribute("default");

  5. }

  6. for(XNode child : context.getChildren()) {

  7. String id = child.getStringAttribute("id");

  8. // 验证 id

  9. if(isSpecifiedEnvironment(id)) {

  10. // 解析 transactionManager, 并实例化 TransactionFactory

  11. TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));

  12. // 解析 dataSource,并实例化 DataSourceFactory

  13. DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));

  14. // 获取 dataSource

  15. DataSource dataSource = dsFactory.getDataSource();

  16. Environment.Builder environmentBuilder = newEnvironment.Builder(id)

  17. .transactionFactory(txFactory)

  18. .dataSource(dataSource);

  19. configuration.setEnvironment(environmentBuilder.build());

  20. }

  21. }

  22. }

  23. }


  24. privateboolean isSpecifiedEnvironment(String id) {

  25. if(environment == null) {

  26. thrownewBuilderException("No environment specified.");

  27. } elseif(id == null) {

  28. thrownewBuilderException("Environment requires an id attribute.");

  29. } elseif(environment.equals(id)) {

  30. returntrue;

  31. }

  32. returnfalse;

  33. }

若没有配置 environment环境或环境没有给 id属性,则会抛出异常,若当前 id是要使用的就返回 true,否则返回 falseTransactionFactory实例化过程比较简单,与创建 DataSourceFactory类似。

数据源的获取

获取数据源,首先得创建 DataSourceFactory,上面使用 DataSourceFactorydsFactory=dataSourceElement(child.evalNode("dataSource"))创建

  1. privateDataSourceFactory dataSourceElement(XNode context) throwsException{

  2. if(context != null) {

  3. String type = context.getStringAttribute("type");

  4. Properties props = context.getChildrenAsProperties();

  5. DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();

  6. factory.setProperties(props);

  7. return factory;

  8. }

  9. thrownewBuilderException("Environment declaration requires a DataSourceFactory.");

  10. }

这里就是获取到数据源得 type后,利用上面所讲到得 resolveClass()方法获取到 DataSourceFactory。以 UNPOOLED为例,对应的 DataSourceFactory实现类为 UnpooledDataSourceFactory。实例化过程中就给该类的属性 dataSource数据源赋值了

  1. /**

  2. * UnpooledDataSourceFactory 类

  3. */

  4. protectedDataSource dataSource;


  5. publicUnpooledDataSourceFactory() {

  6. this.dataSource = newUnpooledDataSource();

  7. }


  8. @Override

  9. publicDataSource getDataSource() {

  10. return dataSource;

  11. }

UnpooledDataSource类里面有静态代码块所以数据源被加载

  1. /**

  2. * UnpooledDataSource 类

  3. */

  4. static{

  5. Enumeration<Driver> drivers = DriverManager.getDrivers();

  6. while(drivers.hasMoreElements()) {

  7. Driver driver = drivers.nextElement();

  8. registeredDrivers.put(driver.getClass().getName(), driver);

  9. }

  10. }

databaseIdProvider 节点解析

databaseIdProvider配置方式
  1. "DB_VENDOR">

  2. "SQL Server" value="sqlserver"/>

  3. "DB2" value="db2"/>

  4. "Oracle" value="oracle"/>

  5. "MySQL" value="mysql"/>


基于映射语句中的 databaseId属性,可以根据不同数据库厂商执行不同的sql。

databaseIdProviderElement()方法
  1. privatevoid databaseIdProviderElement(XNode context) throwsException{

  2. DatabaseIdProvider databaseIdProvider = null;

  3. if(context != null) {

  4. String type = context.getStringAttribute("type");

  5. // 保持向后兼容

  6. if("VENDOR".equals(type)) {

  7. type = "DB_VENDOR";

  8. }

  9. Properties properties = context.getChildrenAsProperties();

  10. databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();

  11. databaseIdProvider.setProperties(properties);

  12. }

  13. Environment environment = configuration.getEnvironment();

  14. if(environment != null&& databaseIdProvider != null) {

  15. String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());

  16. configuration.setDatabaseId(databaseId);

  17. }

  18. }

根据匹配的数据库厂商类型匹配数据源 databaseIdProvider.getDatabaseId(environment.getDataSource())

  1. @Override

  2. publicString getDatabaseId(DataSource dataSource) {

  3. if(dataSource == null) {

  4. thrownewNullPointerException("dataSource cannot be null");

  5. }

  6. try{

  7. return getDatabaseName(dataSource);

  8. } catch(Exception e) {

  9. log.error("Could not get a databaseId from dataSource", e);

  10. }

  11. returnnull;

  12. }


  13. privateString getDatabaseName(DataSource dataSource) throwsSQLException{

  14. // 根据数据源获取数据库产品名称

  15. String productName = getDatabaseProductName(dataSource);

  16. if(this.properties != null) {

  17. for(Map.Entry<Object, Object> property : properties.entrySet()) {

  18. // 判断是否包含,选择使用的数据库产品

  19. if(productName.contains((String) property.getKey())) {

  20. return(String) property.getValue();

  21. }

  22. }

  23. // no match, return null

  24. returnnull;

  25. }

  26. return productName;

  27. }


  28. privateString getDatabaseProductName(DataSource dataSource) throwsSQLException{

  29. Connection con = null;

  30. try{

  31. // 数据库连接

  32. con = dataSource.getConnection();

  33. // 获取连接元数据

  34. DatabaseMetaData metaData = con.getMetaData();

  35. // 获取数据库产品名称

  36. return metaData.getDatabaseProductName();

  37. } finally{

  38. if(con != null) {

  39. try{

  40. con.close();

  41. } catch(SQLException e) {

  42. // ignored

  43. }

  44. }

  45. }

  46. }

这里需要注意的是配置:比如使用 mysql,我踩过这里的坑,这里Name为 MySQL,我把 y写成大写,结果匹配不上。另外这里写个 My也能匹配上,应为是使用的 String.contains方法,只要包含就会符合,这里代码应该不够严谨。

typeHandlers 节点解析

typeHandlers配置方式
  1. <package name="com.ytao.main.handler"/>

  2. // 或

  3. "java.util.Date" jdbcType="TIMESTAMP" handler="com.ytao.main.handler.DemoDateHandler"/>

扫描整个包或者指定类型之间的映射, javaType, jdbcType非必需, handler必填项

typeHandlerElement()方法
  1. privatevoid typeHandlerElement(XNode parent) throwsException{

  2. if(parent != null) {

  3. for(XNode child : parent.getChildren()) {

  4. if("package".equals(child.getName())) {

  5. // 获取包名

  6. String typeHandlerPackage = child.getStringAttribute("name");

  7. // 注册包下所有的类型处理器

  8. typeHandlerRegistry.register(typeHandlerPackage);

  9. } else{

  10. String javaTypeName = child.getStringAttribute("javaType");

  11. String jdbcTypeName = child.getStringAttribute("jdbcType");

  12. String handlerTypeName = child.getStringAttribute("handler");

  13. Class javaTypeClass = resolveClass(javaTypeName);

  14. JdbcType jdbcType = resolveJdbcType(jdbcTypeName);

  15. Class typeHandlerClass = resolveClass(handlerTypeName);

  16. if(javaTypeClass != null) {

  17. if(jdbcType == null) {

  18. typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);

  19. } else{

  20. typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);

  21. }

  22. } else{

  23. typeHandlerRegistry.register(typeHandlerClass);

  24. }

  25. }

  26. }

  27. }

  28. }

源码分析会根据包下所有处理器或者指定处理器进行解析,最后会根据上面分析到的 type+handlertype+jdbc type+handler不同情况注册。另外这里还有个 TypeHandlerRegistry.register(ClasstypeHandlerClass)注册类

  1. publicvoidregister(Class typeHandlerClass) {

  2. // 标志是否从 MappedTypes 注解中获取 javaType 注册

  3. boolean mappedTypeFound = false;

  4. // 获取 MappedTypes 的值

  5. MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);

  6. if(mappedTypes != null) {

  7. for(Class javaTypeClass : mappedTypes.value()) {

  8. // 已 type + handler 的方式注册

  9. register(javaTypeClass, typeHandlerClass);

  10. // 标志已通过注解注册类型

  11. mappedTypeFound = true;

  12. }

  13. }

  14. if(!mappedTypeFound) {

  15. // 通过 TypeHandler 注册

  16. register(getInstance(null, typeHandlerClass));

  17. }

  18. }


  19. // 实例化

  20. public TypeHandler getInstance(Class javaTypeClass, Class typeHandlerClass) {

  21. if(javaTypeClass != null) {

  22. try{

  23. // 获取有参构造函数

  24. Constructor c = typeHandlerClass.getConstructor(Class.class);

  25. // 实例化对象

  26. return(TypeHandler) c.newInstance(javaTypeClass);

  27. } catch(NoSuchMethodException ignored) {

  28. // ignored

  29. } catch(Exception e) {

  30. thrownewTypeException("Failed invoking constructor for handler "+ typeHandlerClass, e);

  31. }

  32. }

  33. try{

  34. // 获取无参构造函数

  35. Constructor c = typeHandlerClass.getConstructor();

  36. return(TypeHandler) c.newInstance();

  37. } catch(Exception e) {

  38. thrownewTypeException("Unable to find a usable constructor for "+ typeHandlerClass, e);

  39. }

  40. }


  41. // 注册实例

  42. public voidregister(TypeHandler typeHandler) {

  43. boolean mappedTypeFound = false;

  44. MappedTypes mappedTypes = typeHandler.getClass().getAnnotation(MappedTypes.class);

  45. if(mappedTypes != null) {

  46. for(Class handledType : mappedTypes.value()) {

  47. register(handledType, typeHandler);

  48. mappedTypeFound = true;

  49. }

  50. }

  51. // @since 3.1.0 - try to auto-discover the mapped type

  52. if(!mappedTypeFound && typeHandler instanceofTypeReference) {

  53. try{

  54. TypeReference typeReference = (TypeReference) typeHandler;

  55. register(typeReference.getRawType(), typeHandler);

  56. mappedTypeFound = true;

  57. } catch(Throwable t) {

  58. // maybe users define the TypeReference with a different type and are not assignable, so just ignore it

  59. }

  60. }

  61. if(!mappedTypeFound) {

  62. register((Class) null, typeHandler);

  63. }

  64. }

以上的 register方法中,了解 type+jdbc type+handler后,其他的 register重载方法比较容易理解,其他的都是基于它上面的封装。

mappers 节点解析

mappers配置方式
  1. <package name="com.ytao.main.mapper"/>

  2. // 或

  3. "mapper/studentMapper.xml"/>

  4. // 或

  5. "file:///D:/mybatis-3-mybatis-3.4.6/src/main/resources/mapper/studentMapper.xml"/>

  6. // 或

  7. class="com.ytao.main.mapper.StudentMapper"/>

可通过以上四种形式配置 mappers节点, 为互斥节点。

mapperElement()方法

该方法是负责解析 节点

  1. privatevoid mapperElement(XNode parent) throwsException{

  2. if(parent != null) {

  3. for(XNode child : parent.getChildren()) {

  4. // 如果配置 package 节点,则扫描

  5. if("package".equals(child.getName())) {

  6. String mapperPackage = child.getStringAttribute("name");

  7. // 解析包下类Mapper接口,并注册到configuration的mapperRegistry中

  8. configuration.addMappers(mapperPackage);

  9. } else{

  10. // 获取mapper节点的resource,url,class属性

  11. String resource = child.getStringAttribute("resource");

  12. String url = child.getStringAttribute("url");

  13. String mapperClass = child.getStringAttribute("class");

  14. // 根据resource解析,并且url,class值必须为空,也就不能配置值。url,class同理,其它两个属性也不能配置值

  15. if(resource != null&& url == null&& mapperClass == null) {

  16. ErrorContext.instance().resource(resource);

  17. // 通过resource获取流

  18. InputStream inputStream = Resources.getResourceAsStream(resource);

  19. // 创建XMLMapperBuilder对象

  20. XMLMapperBuilder mapperParser = newXMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());

  21. // 解析映射配置文件

  22. mapperParser.parse();

  23. } elseif(resource == null&& url != null&& mapperClass == null) {

  24. ErrorContext.instance().resource(url);

  25. // 通过url获取流

  26. InputStream inputStream = Resources.getUrlAsStream(url);

  27. // 和resource解析方式一样,创建XMLMapperBuilder对象,然后解析映射配置文件

  28. XMLMapperBuilder mapperParser = newXMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());

  29. mapperParser.parse();

  30. } elseif(resource == null&& url == null&& mapperClass != null) {

  31. // 加载class属性的接口

  32. Class mapperInterface = Resources.classForName(mapperClass);

  33. // 将接口注册到configuration的mapperRegistry中

  34. configuration.addMapper(mapperInterface);

  35. } else{

  36. thrownewBuilderException("A mapper element may only specify a url, resource or class, but not more than one.");

  37. }

  38. }

  39. }

  40. }

  41. }

的包扫描到的类,然后单个单个注册到configuration的mapperRegistry中,这里和 使用 class属性是一样逻辑。解析 package方式

  1. // Configuration 中定义了

  2. protectedfinalMapperRegistry mapperRegistry = newMapperRegistry(this);


  3. /**

  4. * 步骤一

  5. * 该函数于 Configuration 中

  6. */

  7. publicvoid addMappers(String packageName) {

  8. // mapperRegistry定义在Configuration中的一个属性

  9. mapperRegistry.addMappers(packageName);

  10. }


  11. /**

  12. * 步骤二

  13. * 该函数于 MapperRegistry 中

  14. */

  15. publicvoid addMappers(String packageName) {

  16. addMappers(packageName, Object.class);

  17. }


  18. /**

  19. * 步骤三

  20. * 该函数于 MapperRegistry 中

  21. */

  22. publicvoid addMappers(String packageName, Class superType) {

  23. // 通过 ResolverUtil 获取包下的类

  24. ResolverUtil<Class> resolverUtil = newResolverUtil<Class>();

  25. resolverUtil.find(newResolverUtil.IsA(superType), packageName);

  26. Set<ClassextendsClass>> mapperSet = resolverUtil.getClasses();

  27. for(Class mapperClass : mapperSet) {

  28. // 遍历获取到的类,注册到 MapperRegistry

  29. addMapper(mapperClass);

  30. }

  31. }


  32. /**

  33. * 步骤四

  34. * 该函数于 MapperRegistry 中

  35. */

  36. public void addMapper(Class type) {

  37. // mapper 类为 interface 接口

  38. if(type.isInterface()) {

  39. // 判断当前class是否已经注册过

  40. if(hasMapper(type)) {

  41. thrownewBindingException("Type "+ type + " is already known to the MapperRegistry.");

  42. }

  43. // 校验是否加载完成

  44. boolean loadCompleted = false;

  45. try{

  46. // 保存 mapper 接口和 MapperProxyFactory 之间的映射

  47. knownMappers.put(type, newMapperProxyFactory(type));

  48. // It's important that the type is added before the parser is run

  49. // otherwise the binding may automatically be attempted by the

  50. // mapper parser. If the type is already known, it won't try.

  51. // 解析xml和注解

  52. MapperAnnotationBuilder parser = newMapperAnnotationBuilder(config, type);

  53. parser.parse();

  54. // 标志加载完成

  55. loadCompleted = true;

  56. } finally{

  57. if(!loadCompleted) {

  58. knownMappers.remove(type);

  59. }

  60. }

  61. }

  62. }

解析 mapperclass属性

  1. // 该函数于 Configuration 中

  2. public void addMapper(Class type) {

  3. mapperRegistry.addMapper(type);

  4. }


  5. // ... 这里调用上面的【步骤四】

这两中方式是直接注册接口到 mapperRegistry,另外两种是解析 xml的方式就是获取映射文件的 namespace,再注册进来, XMLMapperBuilder是负责解析映射配置文件的类,今后会单独详细分析这个类,这里不展开讲。

这里对XMLConfigBuilder解析配置文件到此分析完,本文对配置文件解析的流程大致了解流程和原理。相信遇到配置问题异常,大致能排查到根本原因

程序员专栏
 扫码关注填加客服 
长按识别下方二维码进群

近期精彩内容推荐:  

 再见,戴威!再见,小黄车!

 别瞎学了,这几门语言要被淘汰了!

 再见 Win10!下一代操作系统来了。。

 墙裂建议收藏,100道Python练手题目




在看点这里好文分享给更多人↓↓

浏览 8
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报