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/ 本文版权归本人和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    17. Letter Combinations of a Phone Number
    16. 3Sum Closest
    15. 3Sum
    14. Longest Common Prefix
    13. Roman to Integer
    12. Integer to Roman
    11. Container With Most Water
    10. Regular Expression Matching
    9. Palindrome Number
    8. String to Integer (atoi)
  • 原文地址:https://www.cnblogs.com/question-sky/p/6602151.html
Copyright © 2011-2022 走看看