zoukankan      html  css  js  c++  java
  • Spring mybatis源码篇章-SqlSessionFactoryBean

    通过实例结合源码的方式解读,其中涉及到的文件来自于笔者的Github毕设项目,引用的jar包为mybatis-spring-1.3.0.jar

    Mybatis

    Mybatis是基于ORM(Object relation mapping)思想而开发的框架插件,本质原理用一句笔者的话便是使用了JAVA连接数据库的方式来执行相应的SQL(PreparedStatement),并在此基础上提供了丰富的动态SQL配置以及缓存等概念。(后续文章会提及)

    其基本上是企业与传统数据库(Oracle/Mysql等)交互常用的数据库持久层框架,以JAVA语言来说,其与Spring的搭配使用也是广为流传。

    本文将从源码的角度浅析Mybatis与Spring的亲密搭配

    范例

    笔者本文只关注SqlSessionFactoryBean的配置,样例如下

        <!--数据源,引用common-pool包的数据源类-->
        <bean id="dataSource-mysql" class="org.apache.commons.dbcp.BasicDataSource"> 
    	    <property name="driverClassName" value="${jdbc.driverClassName}" /> 
    	    <property name="url" value="${jdbc.url}" /> 
    	    <property name="username" value="${jdbc.username}" /> 
    	    <property name="password" value="${jdbc.password}" /> 
    	    <property name="initialSize" value="1" /> 
    	    <property name="maxActive" value="20" /> 
    	</bean> 
    
        <!--sqlSessionFactoryBean创建--> 
        <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> 
    	    <property name="dataSource" ref="dataSource-mysql" /> 
    	    <property name="typeAliasesPackage" value="com.du.wx.model" /> 
    	    <property name="mapperLocations" value="classpath:com/du/wx/resources/mapper/*.xml"/> 
    	</bean> 
    
        <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> 
    	    <!-- joggle代表接口 --> 
    	    <property name="basePackage" value="com.du.wx.mapper.joggle" /> 
        </bean>
    

    由上述的配置可知,在配置SqlSessionFactory数据库会话工厂Bean对象时必须依赖相应的数据源属性dataSource

    SqlSessionFactoryBean

    笔者分层次来解析此类

    基础内部属性

    优先观察SqlSessionFactoryBean的内部属性,代码片段如下

        private static final Log LOGGER = LogFactory.getLog(SqlSessionFactoryBean.class);
    
        private Resource configLocation;
    
        private Configuration configuration;
    
        private Resource[] mapperLocations;
    
        private DataSource dataSource;
    
        private TransactionFactory transactionFactory;
    
        private Properties configurationProperties;
    
        private SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
    
        private SqlSessionFactory sqlSessionFactory;
    
        //EnvironmentAware requires spring 3.1
        private String environment = SqlSessionFactoryBean.class.getSimpleName();
    
        private boolean failFast;
    
        private Interceptor[] plugins;
    
        private TypeHandler<?>[] typeHandlers;
    
        private String typeHandlersPackage;
    
        private Class<?>[] typeAliases;
    
        private String typeAliasesPackage;
    
        private Class<?> typeAliasesSuperType;
    
        //issue #19. No default provider.
        private DatabaseIdProvider databaseIdProvider;
    
        private Class<? extends VFS> vfs;
    
        private Cache cache;
    
        private ObjectFactory objectFactory;
    
        private ObjectWrapperFactory objectWrapperFactory;
    

    常用的属性如下:

    configLocation mybatis主配置文件路径,支持classpath语法

    mapperLocations 指定mybatis的mapper配置文件,支持classpath语法

    dataSource 数据源

    typeAliasesPackage 指定model层类名的别名扫描包,这与mapper配置中的parameterTyperesultType搭配使用

    InitializingBean复写方法

    紧接着看下其复写的afterPropertiesSet()方法,代码如下

      @Override
      public void afterPropertiesSet() throws Exception {
        //datasource not allowed to be null.
        notNull(dataSource, "Property 'dataSource' is required");
        notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
        state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
                  "Property 'configuration' and 'configLocation' can not specified with together");
    
        this.sqlSessionFactory = buildSqlSessionFactory();
      } 
    

    与前文提及的对应,必须确保dataSource属性已经得到配置。

    实例化步骤

    最后查看关键实例方法buildSqlSessionFactory()方法,由于片段过长,笔者截取几段来进行讲解


    Mybatis主文件加载

    	Configuration configuration;
    
        XMLConfigBuilder xmlConfigBuilder = null;
        if (this.configuration != null) {
          configuration = this.configuration;
          if (configuration.getVariables() == null) {
            configuration.setVariables(this.configurationProperties);
          } else if (this.configurationProperties != null) {
            configuration.getVariables().putAll(this.configurationProperties);
          }
        } else if (this.configLocation != null) {
          //如果spring配置中configLocation属性不为空,则加载指定的Mybatis配置
          xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
          configuration = xmlConfigBuilder.getConfiguration();
        } else {
          //否则则采用默认的Mybatis配置,@see Configuration
          if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Property `configuration` or 'configLocation' not specified, using default MyBatis Configuration");
          }
          configuration = new Configuration();
          configuration.setVariables(this.configurationProperties);
        }
    

    通过上述配置表明,内部属性configLocation是非必须配置项


    model别名映射扫描

    	if (hasLength(this.typeAliasesPackage)) {
          //截取typeAliasesPackage属性的包,支持,或;
          String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
          for (String packageToScan : typeAliasPackageArray) {
    		//对应方法的目的是通过扫描包得到其包以及子包目录下的所有Class,然后为每个class注册别名  
            configuration.getTypeAliasRegistry().registerAliases(packageToScan,
            typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
            if (LOGGER.isDebugEnabled()) {
              LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
            }
          }
        }
    

    即对内部属性typeAliasesPackage支持,;作为分隔符以加载多个目录,主要是为了方便编程人员使用parameterType/resultType等属性


    mapper配置文件加载

    	if (!isEmpty(this.mapperLocations)) {
          //mapperLocations
          for (Resource mapperLocation : this.mapperLocations) {
            if (mapperLocation == null) {
              continue;
            }
    
            try {
              //对扫描的每个配置文件进行解析,并保存其中的必要参数,后续会分析
              XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
                  configuration, mapperLocation.toString(), configuration.getSqlFragments());
              xmlMapperBuilder.parse();
            } catch (Exception e) {
              throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
            } finally {
              ErrorContext.instance().reset();
            }
    
            if (LOGGER.isDebugEnabled()) {
              LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
            }
          }
        } else {
          if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
          }
        }
    

    关键处理XML配置的mapper文件,用到了mybatis包中的org.apache.ibatis.builder.xml.XMLMapperBuilder类进行解析。这个后续分析


    Configuration-所有关于Mybatis的信息均存储在Mybatis包中的org.apache.ibatis.session.Configuration类上

    return this.sqlSessionFactoryBuilder.build(configuration);
    

    Spring呢,就通过SqlSessionFactoryBuilder类来创建相应的SqlSessionFactory对象。具体的由于篇幅过长,就放在后文分析。

    总结

    从上文便可以看出来Spring框架是如何整合Mybatis的,就是这个Mybatis的关键类Configuration。而相应的开关数据库等一系列的功能都是通过Spring的SqlSessionFactory对象来操控的。这也是两者结合最核心的地方

    作者:南柯问天 出处:http://www.cnblogs.com/question-sky/ 本文版权归本人和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    SVN tags使用
    switch case执行顺序
    excel 15位身份证转18位
    .net core options 依赖注入的方式
    DICOM 相关概念了解
    Dicom文件基本操作
    asp.net core 3 使用nlog日志组件,使用$ {basedir}保存位置不对,记录下怎么解决
    asp.net core 动态更新 appsetting.json方法
    asp.net core appsetting.json 绑定读取
    centos 安装 nginx 及配置 的坑
  • 原文地址:https://www.cnblogs.com/question-sky/p/6602151.html
Copyright © 2011-2022 走看看