基于注解配置mybatis及如何解析mybatis的配置文件

本篇文章讲述的基于注解配置mybatis及如何解析mybatis的配置文件,下一章会讲解mybatis如何具体结合spring来生成对应的bean代理对象

@Configuration("dataSourceConfig")//定义bean配置
@MapperScan(basePackages = DataSourceConfig.DaoPackage, sqlSessionFactoryRef = "SqlSessionFactory")设置mybatis接口扫描包路径,设置引入的sessionFactory的bean
public class DataSourceConfig {//定义mybatis数据源配置

    // 针对对应的dao包路径来关联数据源,这样在多数据源时有用
   public static final String DaoPackage = "com.biz.dao";
   public static final String SqlMapLocation = "classpath*:/sqlmap/*.xml";

    @Value("${mdc.db.url}")
    private String url;

    @Value("${mdc.db.username}")
    private String user;

    @Value("${mdc.db.password}")
    private String password;

    @Value("${mdc.db.driver}")
    private String driverClass;

    @Value("${mdc.db.initialSize}")
    private int initialSize;

    @Value("${mdc.db.minIdle}")
    private int minIdle;

    @Value("${mdc.db.maxActive}")
    private int maxActive;

    @Value("${mdc.db.maxWait}")
    private long maxWait;

    @Bean(name = "DataSource")
    public DataSource dataSource() {//数据源具体配置并生成bean(带连接池的数据源)
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driverClass);
        dataSource.setUrl(url);
        dataSource.setUsername(user);
        dataSource.setPassword(password);
        dataSource.setInitialSize(initialSize);
        dataSource.setMinIdle(minIdle);
        dataSource.setMaxActive(maxActive);
        dataSource.setMaxWait(maxWait);
        return dataSource;
    }

    @Bean(name = "TransactionManager")//定义事务管理bean
    public DataSourceTransactionManager transactionManager(@Qualifier("DataSource") DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);//内部方法逻辑在下面解释
    }

public DataSourceTransactionManager(DataSource dataSource) {
    this();//调用默认无参构造方法
    this.setDataSource(dataSource);//设置数据源
    this.afterPropertiesSet();//判断数据源是否设置成功
}
public DataSourceTransactionManager() {//默认无参数构造器
    this.enforceReadOnly = false;//强制只读置为false
    this.setNestedTransactionAllowed(true);//是否允许嵌套事务为true
}
public void setDataSource(DataSource dataSource) {//设置数据源
    if (dataSource instanceof TransactionAwareDataSourceProxy) {//判断是否为带代理的数据源(我们这里不是)
        this.dataSource = ((TransactionAwareDataSourceProxy)dataSource).getTargetDataSource();//如果是代理数据源则获取对应的代理对象
    } else {//否则直接设置数据源
        this.dataSource = dataSource;
    }

}
public void afterPropertiesSet() {//判断数据源是否设置成功
    if (this.getDataSource() == null) {//如果数据源为null则抛异常
        throw new IllegalArgumentException("Property 'dataSource' is required");
    }
}

 

    @Bean(name = "SqlSessionFactory")
    public SqlSessionFactory masterSqlSessionFactory(@Qualifier("mdcDataSource") DataSource dataSource)
            throws Exception {//创建sqlSessionFactory的bean
     final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();//实例化SqlSessionFactoryBean对象        sessionFactory.setDataSource(dataSource);//设置数据源,逻辑与事务管理设置数据源一致,这里就不说了
        sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver()
                .getResources(DataSourceConfig.SqlMapLocation)//这里是从mybatis的xml路径里加载指定的资源文件);//将解析后返回的Resources数组放在SqlSessionFactoryBean对象里,便于后面进行解析
        return sessionFactory.getObject();//获取具体的SqlSessionFactory对象
    }
public Resource[] getResources(String locationPattern) throws IOException {//根据指定路径读取资源文件并封装成Resource[]
    Assert.notNull(locationPattern, "Location pattern must not be null");//断言非空判断
    if (locationPattern.startsWith("classpath*:")) {//如果路径配置在classpath下
        return this.getPathMatcher().isPattern(locationPattern.substring("classpath*:".length())) ? this.findPathMatchingResources(locationPattern) : this.findAllClassPathResources(locationPattern.substring("classpath*:".length()));//匹配判断(如果有通配符(*或者?)则根据通配符进行查找,否则直接根据具体的路径去查找)
    } else {//如果不是classpath路径
        int prefixEnd = locationPattern.startsWith("war:") ? locationPattern.indexOf("*/") + 1 : locationPattern.indexOf(":") + 1;//获取要解析的起始下标
        return this.getPathMatcher().isPattern(locationPattern.substring(prefixEnd)) ? this.findPathMatchingResources(locationPattern) : new Resource[]{this.getResourceLoader().getResource(locationPattern)};//根据路径去加载
    }
}
public boolean isPattern(String path) {//根据指定path来进行匹配
    return path.indexOf(42) != -1 || path.indexOf(63) != -1;//如果不存在42的index和63的index返回false,否则true(在ASSCII码表中42代表*63代表?,这里调用的是String类里的indexOf方法,String底层是由char数组构成的)
}

public SqlSessionFactory getObject() throws Exception {//获取SqlSessionFactory对象

if (this.sqlSessionFactory == null) {//如果是第一次创建

this.afterPropertiesSet(); //调用创建方法

}

return this.sqlSessionFactory;//返回SqlSessionFactory对象

}

public void afterPropertiesSet() throws Exception {//创建SqlSessionFactory
    Assert.notNull(this.dataSource, "Property 'dataSource' is required");//判断数据源有没有配置,如果为null抛异常
    Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");//判断sqlSessionFactoryBuilder有没有生成,如果为null抛异常(可以看出来,这里用到了建造者模式和工程模式)
    Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");//这里判断相关对象是否为空,理论上是第一次创建sqlSessionFactory对象的话,这些对象暂时都是null,否则抛异常
    this.sqlSessionFactory = this.buildSqlSessionFactory();调用建造者方法创建sqlSessionFactory
}
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {//建造SqlSessionFactory方法
    XMLConfigBuilder xmlConfigBuilder = null;//定义解析xml的对象
    Configuration configuration;
    if (this.configuration != null) {//如果configuration非空(第一次创建时理论上都是null)
        configuration = this.configuration;//获取configuration对象
configuration是一个配置类,里面有以下属性:
(
cacheEnabled:
全局开启或关闭配置文件中所有 mapper 已经配置的缓存。默认值:true

lazyLoadingEnabled:
全局开启或关闭延迟加载。开启时,所有关联对象都会延迟加载。特定关联关系中可以通过设置 fetchType 属性来取覆盖该项的开关状态。默认值:false

aggressiveLazyLoading:
开启或关闭属性的延迟加载。开启时,任何方法调用都将加载该对象的所有延迟属性。关闭时,每个属性将按需加载(参考 lazyLoadTriggerMethods)。默认:false

multipleResultSetsEnabled:
是否允许单个语句中返回多个结果集(需要兼容驱动)。默认:true

useColumnLabe:
使用列别名替代列名。不同驱动在这方面表现不同。具体可参考驱动文档,或者通过测试判断这两种模式下不同驱动的表现。默认:true

useGeneratedKeys:
描述:是否允许 JDBC 支持主键自动生成。需要兼容驱动。若设置为 true,则强制使用主键自动生成,尽管一些驱动不兼容也可以正常工作(如 Derby)。 默认:false

autoMappingBehavior:
指定 Mybatis 是否自动映射列到字段或属性。默认值:PARTIAL
NONE:不允许自动映射。
PARTIAL:只自动映射没有定义嵌套结果集映射的结果集。
FULL:自动映射任意复杂的结果集(无论是否嵌套)

autoMappingUnknownColumnBehavior:
指定发现自动映射目标未知列(或未知属性类型)的行为。默认值:NONE
NONE:什么也不做。
WARNING:输出警告日志( ‘org.apache.ibatis.session.AutoMappingUnknownColumnBehavior’ 的日志级别要设置为 WARN)。
FAILING:映射失败(抛出 SqlSessionException 异常)。

defaultExecutorType:
配置默认执行器。默认值:SIMPLE
SIMPLE:普通的执行器。
REUSE:重复执行预处理语句(prepared statements)。
BATCH:重复执行语句并执行批量更新。

defaultStatementTimeout:
设置驱动等待数据库响应的超时秒数。默认值:Not Set (null)

defaultFetchSize:
为驱动的结果集获取数量设置一个提示值。这个参数值可以被查询设置覆盖。默认值:Not Set (null)

safeRowBoundsEnabled
:允许在嵌套语句中使用分页 RowBound。如果允许,设置为 false。默认值:false

safeResultHandlerEnabled:
允许在嵌套语句中使用 ResultHandler。如果允许,设置为 false;默认值:true

mapUnderscoreToCamelCase:
开启驼峰规则命名的自动映射,即从经典数据库列名命名 A_COLUMN 到经典 Java 属性命名 aColumn 的自动映射。默认值:false

localCacheScope:
:Mybatis 使用本地缓存机制(local cache)来防止循环引用和加速重复嵌套查询。默认值 session
SESSION:一个 session 期间的所有查询执行都将被缓存。
STATEMENT:本地 session 仅在语句执行时使用,且对同一个 session 的不同调用不会共享数据。

jdbcTypeForNull:
未对参数指定 JDBC 类型时,当参数为空值时指定 JDBC 类型。某些驱动要求指定列的 JDBC 类型,某些驱动用一般类型,比如 NULL、VARCHAR 或 OTOthHER 即可。默认值:OTOthHER

lazyLoadTriggerMethods:
指定对象的哪些方法触发一次延迟加载。默认值:equals,clone,hashCode,toString

defaultScriptingLanguage:
指定动态 SQL 生成的默认语言。默认值:org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
可选值:A type alias or fully qualified class name.类型别名或完全限定类名

defaultEnumTypeHandler:
指定 Enum 默认使用的 TypeHandler(从3.4.5开始)
可选值:A type alias or fully qualified class name. //类型别名或完全限定类名

callSettersOnNulls:
指定当结果集中值为空时是否调用映射对象的 setter 方法或 map 的 put 方法。如果你依赖于 Map.keySet() 或者在 null 值初始化时,该设置有用。注意,基本类型比如 int 、boolean 等不能被设置为 null;默认:false

returnInstanceForEmptyRow:
当返回行的所有列都是 null 时,Mybatis 默认返回 null 。当该设置被开启时,Mybatis 会返回一个空实例。注意这将同样适用于嵌套结果集(如 collection 和 association)。从 3.4.2 开始。默认:false

logPrefix:
指定 Mybatis 添加到日志名称的前缀。默认值:Not Set

logImpl:
指定 Mybatis 使用的日志工具。如果此项未设置,将会自动查找日志工具。默认值:Not Set
可选值:SLF4J | LOG4J | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING

proxyFactory:
指定 Mybatis 创建具有懒加载能力的对象的代理工具;默认值:JAVASSIST (MyBatis 3.3 or above)

vfsImpl:
指定 VFS 的具体实现。默认值:Not Set
可选值:Fully qualified class names of custom VFS implementation separated by commas. //逗号分隔的自定义 VFS 具体实现的完全限定类名。

useActualParamName:
允许将方法签名中定义的真实名称作为语句参数名称。为了使用这一特性,你的项目必须采用 java 8 编译,且必须加上 -parameters 选项。(从 3.4.1 开始)默认值:true

configurationFactory:
指定提供 Configuration 实例的类。返回的 Configuration 实例用来加载被反序列化对象的懒加载属性。这个类必须有一个 static Configuration getConfiguration() 签名方法。(从 3.2.3 开始)默认值:Not Set
可选值:A type alias or fully qualified class name. //类型别名或完全限定类名


  1. protected Environment environment;
  2. // 允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为false。默认为false
  3. protected boolean safeRowBoundsEnabled;
  4. // 允许在嵌套语句中使用分页(ResultHandler)。如果允许使用则设置为false。
  5. protected boolean safeResultHandlerEnabled = true;
  6. // 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。默认false
  7. protected boolean mapUnderscoreToCamelCase;
  8. // 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性会按需加载。默认值false (true in ≤3.4.1)
  9. protected boolean aggressiveLazyLoading;
  10. // 是否允许单一语句返回多结果集(需要兼容驱动)。
  11. protected boolean multipleResultSetsEnabled = true;
  12.  
  13. // 允许 JDBC 支持自动生成主键,需要驱动兼容。这就是insert时获取mysql自增主键/oracle sequence的开关。注:一般来说,这是希望的结果,应该默认值为true比较合适。
  14. protected boolean useGeneratedKeys;
  15.  
  16. // 使用列标签代替列名,一般来说,这是希望的结果
  17. protected boolean useColumnLabel = true;
  18.  
  19. // 是否启用缓存
  20. protected boolean cacheEnabled = true;
  21.  
  22. // 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的。
  23. protected boolean callSettersOnNulls;
  24.  
  25. // 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用Java 8编译,并且加上-parameters选项。(从3.4.1开始)
  26. protected boolean useActualParamName = true;
  27.  
  28. //当返回行的所有列都是空时,MyBatis默认返回null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集 (i.e. collectioin and association)。(从3.4.2开始) 注:这里应该拆分为两个参数比较合适, 一个用于结果集,一个用于单记录。通常来说,我们会希望结果集不是null,单记录仍然是null
  29. protected boolean returnInstanceForEmptyRow;
  30.  
  31. // 指定 MyBatis 增加到日志名称的前缀。
  32. protected String logPrefix;
  33.  
  34. // 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。一般建议指定为slf4j或log4j
  35. protected Class <? extends Log> logImpl;
  36.  
  37. // 指定VFS的实现, VFS是mybatis提供的用于访问AS内资源的一个简便接口
  38. protected Class <? extends VFS> vfsImpl;
  39.  
  40. // MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。
  41. protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
  42.  
  43. // 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。
  44. protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
  45.  
  46. // 指定对象的哪个方法触发一次延迟加载。
  47. protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
  48.  
  49. // 设置超时时间,它决定驱动等待数据库响应的秒数。默认不超时
  50. protected Integer defaultStatementTimeout;
  51.  
  52. // 为驱动的结果集设置默认获取数量。
  53. protected Integer defaultFetchSize;
  54.  
  55. // SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。
  56. protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
  57.  
  58. // 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。
  59. protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
  60.  
  61. // 指定发现自动映射目标未知列(或者未知属性类型)的行为。这个值应该设置为WARNING比较合适
  62. protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
  63.  
  64. // settings下的properties属性
  65. protected Properties variables = new Properties();
  66.  
  67. // 默认的反射器工厂,用于操作属性、构造器方便
  68. protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
  69.  
  70. // 对象工厂, 所有的类resultMap类都需要依赖于对象工厂来实例化
  71. protected ObjectFactory objectFactory = new DefaultObjectFactory();
  72.  
  73. // 对象包装器工厂,主要用来在创建非原生对象,比如增加了某些监控或者特殊属性的代理类
  74. protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
  75.  
  76. // 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。
  77. protected boolean lazyLoadingEnabled = false;
  78.  
  79. // 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。MyBatis 3.3+使用JAVASSIST
  80. protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
  81.  
  82. // MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。
  83. protected String databaseId;
  84.  
  85. /**
  86. * Configuration factory class.
  87. * Used to create Configuration for loading deserialized unread properties.
  88. * 指定一个提供Configuration实例的类. 这个被返回的Configuration实例是用来加载被反序列化对象的懒加载属性值. 这个类必须包含一个签名方法static Configuration getConfiguration(). (从 3.2.3 版本开始)
  89. */
  90. protected Class<?> configurationFactory;
  91.  
  92. protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
  93.  
  94. // mybatis插件列表
  95. protected final InterceptorChain interceptorChain = new InterceptorChain();
  96. protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
  97.  
  98. // 类型注册器, 用于在执行sql语句的出入参映射以及mybatis-config文件里的各种配置比如<transactionManager type="JDBC"/><dataSource type="POOLED">时使用简写, 后面会详细解释
  99. protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
  100. protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
  101.  
  102. protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
  103. protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
  104. protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
  105. protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
  106. protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");
  107.  
  108. protected final Set<String> loadedResources = new HashSet<String>();
  109. protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");
  110.  
  111. protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
  112. protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
  113. protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
  114. protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();
  115.  
  116. /*
  117. * A map holds cache-ref relationship. The key is the namespace that
  118. * references a cache bound to another namespace and the value is the
  119. * namespace which the actual cache is bound to.
  120. */
  121. protected final Map<String, String> cacheRefMap = new HashMap<String, String>()
)
        if (configuration.getVariables() == null) {//如果properties属性为为空
            configuration.setVariables(this.configurationProperties);//用当前对象里的properties对象为其赋值
        } else if (this.configurationProperties != null) {//如果不为空
            configuration.getVariables().putAll(this.configurationProperties);//将当前对象里的properties对象全部存到properties对象里
        }
    } else if (this.configLocation != null) {//如果当前对象的configLocation不为空
        xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), (String)null, this.configurationProperties);//直接创建xmlConfigBuilder对象并解析configLocation对象
        configuration = xmlConfigBuilder.getConfiguration();获取Configuration对象,该对象会在调用上面的方法里被创建
    } else {//如果为空
        if (LOGGER.isDebugEnabled()) {//如果日志级别 为debug则会输出打印日志,使用mybatis默认Configuration配置
            LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
        }

        configuration = new Configuration();//创建Configuration对象
        if (this.configurationProperties != null) {//如果当前对象properties不为空
            configuration.setVariables(this.configurationProperties);//设置properties
        }
    }

    if (this.objectFactory != null) {//如果当前对象的objectFactory对象不为空
        configuration.setObjectFactory(this.objectFactory);//赋值
    }

    if (this.objectWrapperFactory != null) {//如果当前对象的objectWrapperFactory对象不为空
        configuration.setObjectWrapperFactory(this.objectWrapperFactory);//赋值
    }

    if (this.vfs != null) {//如果当前对象的vfs对象不为空(VFS是mybatis提供的用于访问AS内资源的一个简便接口)
        configuration.setVfsImpl(this.vfs);//赋值
    }

    String[] typeHandlersPackageArray;//定义一个类型处理包名的数组
    String[] var4;
    int var5;
    int var6;
    String packageToScan;//扫描包路径
    if (StringUtils.hasLength(this.typeAliasesPackage)) {//如果扫描包路径不为空
        typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeAliasesPackage, ",; \t\n");//将多个包路径转成数组
        var4 = typeHandlersPackageArray;//包路径数组对象
        var5 = typeHandlersPackageArray.length;//数组长度

        for(var6 = 0; var6 < var5; ++var6) {//遍历
            packageToScan = var4[var6];//获取单个要扫描的自定义包路径
            configuration.getTypeAliasRegistry().registerAliases(packageToScan, this.typeAliasesSuperType == null ? Object.class : this.typeAliasesSuperType);//循环添加别名注册扫描包信息
            if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输入日志
                LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
            }
        }
    }

    int var27;
    if (!ObjectUtils.isEmpty(this.typeAliases)) {//如果当前对象里的typeAliases不为空
        Class[] var25 = this.typeAliases;
        var27 = var25.length;

        for(var5 = 0; var5 < var27; ++var5) {//遍历进行注册类别别名信息
            Class<?> typeAlias = var25[var5];
            configuration.getTypeAliasRegistry().registerAlias(typeAlias);
            if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输入日志
                LOGGER.debug("Registered type alias: '" + typeAlias + "'");
            }
        }
    }

    if (!ObjectUtils.isEmpty(this.plugins)) {//插件对象不为空 即有添加自定义拦截器
        Interceptor[] var26 = this.plugins;
        var27 = var26.length;

        for(var5 = 0; var5 < var27; ++var5) {//遍历
            Interceptor plugin = var26[var5];
            configuration.addInterceptor(plugin);//添加拦截器到configuration对象,便于后续调用(责任链模式)
            if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输入日志
                LOGGER.debug("Registered plugin: '" + plugin + "'");
            }
        }
    }

    if (StringUtils.hasLength(this.typeHandlersPackage)) {//如果当前对象里存在typeHandlersPackage
        typeHandlersPackageArray = StringUtils.tokenizeToStringArray(this.typeHandlersPackage, ",; \t\n");//转成数组
        var4 = typeHandlersPackageArray;//包路径数组
        var5 = typeHandlersPackageArray.length;//数组长度

        for(var6 = 0; var6 < var5; ++var6) {//遍历注册
            packageToScan = var4[var6];
            configuration.getTypeHandlerRegistry().register(packageToScan);
            if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输出日志
                LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
            }
        }
    }

    if (!ObjectUtils.isEmpty(this.typeHandlers)) {//如果当前对象的typeHandlers(基于注解配置的别名类型)不为空
        TypeHandler[] var28 = this.typeHandlers;//数组
        var27 = var28.length;//数组长度

        for(var5 = 0; var5 < var27; ++var5) {//遍历注册
            TypeHandler<?> typeHandler = var28[var5];
            configuration.getTypeHandlerRegistry().register(typeHandler);
            if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输出日志
                LOGGER.debug("Registered type handler: '" + typeHandler + "'");
            }
        }
    }

    if (this.databaseIdProvider != null) {//如果数据库提供方的id不为空
        try {
            configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));//设置数据库id
        } catch (SQLException var24) {//异常则报错
            throw new NestedIOException("Failed getting a databaseId", var24);
        }
    }

    if (this.cache != null) {//如果当前对象缓存设置不为空
        configuration.addCache(this.cache);//设置缓存
    }

    if (xmlConfigBuilder != null) {//xmlConfigBuilder对象不为空
        try {
            xmlConfigBuilder.parse();//解析xml文件
            if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输出日志
                LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
            }
        } catch (Exception var22) {//异常则报错
            throw new NestedIOException("Failed to parse config resource: " + this.configLocation, var22);
        } finally {
            ErrorContext.instance().reset();//始终进行重置
        }
    }

    if (this.transactionFactory == null) {//如果事务工厂为空
        this.transactionFactory = new SpringManagedTransactionFactory();//默认使用Spring事务管理工厂
    }

    configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));//设置配置环境
    if (!ObjectUtils.isEmpty(this.mapperLocations)) {//如果mapperLocations对象不为空,其实本质上是resource数组不为空
        Resource[] var29 = this.mapperLocations;//获取数组对象
        var27 = var29.length;//数组长度

        for(var5 = 0; var5 < var27; ++var5) {//遍历数组
            Resource mapperLocation = var29[var5];
            if (mapperLocation != null) {//如果不为空
                try {
                    XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(), configuration, mapperLocation.toString(), configuration.getSqlFragments());//创建XMLMapperBuilder对象
                    xmlMapperBuilder.parse();//解析Resource对象
                } catch (Exception var20) {//异常则报错
                    throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", var20);
                } finally {
                    ErrorContext.instance().reset();//始终重置
                }

                if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输出日志
                    LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
                }
            }
        }
    } else if (LOGGER.isDebugEnabled()) {//如果日志级别为debug则输出日志
        LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
    }

    return this.sqlSessionFactoryBuilder.build(configuration);//调用具体的build方法
}

 

public SqlSessionFactory build(Configuration config) {//具体的build方法
    return new DefaultSqlSessionFactory(config);//从这里可以看出返回的是DefaultSqlSessionFactory对象
}

 

public void parse() {//解析resource
    if (!this.configuration.isResourceLoaded(this.resource)) {//判断有没有被加载过,没有加载过则进行解析,第一次创建SqlSessionFactory默认是没有
        this.configurationElement(this.parser.evalNode("/mapper"));//获取mapper节点内的所有节点并解析
        this.configuration.addLoadedResource(this.resource);//将加载过的resource放在已加载过的set集合里,避免重复加载
        this.bindMapperForNamespace();//根据nameSpace进行mapper类型绑定
    }

    this.parsePendingResultMaps();
    this.parsePendingCacheRefs();
    this.parsePendingStatements();
}

 

private void configurationElement(XNode context) {
    try {
        String namespace = context.getStringAttribute("namespace");//获取mapper节点里的namespace元素
        if (namespace != null && !namespace.equals("")) {//如果namespace元素不为null或者空字符串
            this.builderAssistant.setCurrentNamespace(namespace);//设置当前namespace
            this.cacheRefElement(context.evalNode("cache-ref"));//设置cache-ref节点
            this.cacheElement(context.evalNode("cache"));//设置cache节点
            this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));//设置parameterMap节点
            this.resultMapElements(context.evalNodes("/mapper/resultMap"));//设置resultMap节点
            this.sqlElement(context.evalNodes("/mapper/sql"));//设置sql节点
            this.buildStatementFromContext(context.evalNodes("select|insert|update|delete"));//设置select|insert|update|delete节点
        } else {//为空则抛异常
            throw new BuilderException("Mapper's namespace cannot be empty");
        }
    } catch (Exception var3) {//解析失败抛异常
        throw new BuilderException("Error parsing Mapper XML. Cause: " + var3, var3);
    }
}

 

private void bindMapperForNamespace() {//类型绑定
    String namespace = this.builderAssistant.getCurrentNamespace();//获取nameSpace,本质上是一个类路径
    if (namespace != null) {//如果不为空
        Class boundType = null;

        try {
            boundType = Resources.classForName(namespace);//利用反射根据类路径获取class
        } catch (ClassNotFoundException var4) {//有异常则忽略,从这里可以看出来就算mapper配置文件里的类路径写的不对也是不会报错的
        }

        if (boundType != null && !this.configuration.hasMapper(boundType)) {//如果不为空并且当前类并没有被加载过
            this.configuration.addLoadedResource("namespace:" + namespace);//放在set集合里,格式namespace:具体的namespace
            this.configuration.addMapper(boundType);//调用addMapper方法
        }
    }

}
public <T> void addMapper(Class<T> type) {//addMapper方法
    this.mapperRegistry.addMapper(type);//调用最终的addMapper方法
}
public <T> void addMapper(Class<T> type) {//最终的addMapper方法
    if (type.isInterface()) {//判断传入的class类型是不是一个接口
        if (this.hasMapper(type)) {//判读是否已经加载过
            throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
        }

        boolean loadCompleted = false;

        try {
            this.knownMappers.put(type, new MapperProxyFactory(type));//创建一个mapper的工厂代理类,并放在map里,key类型是class
            MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);//这里开启对查询注解的支持
            parser.parse();//具体进行解析
            loadCompleted = true;//加载完成
        } finally {
            if (!loadCompleted) {//未加载完成
                this.knownMappers.remove(type);//将当前class从map里移除
            }

        }
    }

}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值