zoukankan      html  css  js  c++  java
  • MyBatis 3源码分析

    Mybatis3.2源码分析:
    一、加载配置文件。
        使用SAX解析配置文件。读取xml配置文件后,调用XMLConfigBuilder.parse()方法,在parse方法中再调用parseConfiguration()方法,对读取到的配置信息保存到BaseBuilder.configuration中。
         propertiesElement(root.evalNode("properties")); //issue #117 read properties first    
            代码分析:此方法实现将属性信息存入Configuration中。
                1、如果properties节点不为空,查找配置节点中子节点的属性文件并赋值给defaults(Properties类型,继承自HashTable,线程安全的,HashTable集成Dictionary,实现Map接口),否则此方法结束。
                2、查询resource和url,注意两者不能同时配置,否则会抛出异常,最后将resource或url的属性putAll 进入 defaults中,
                3、将defaults变量存入Configuration中去。

          typeAliasesElement(root.evalNode("typeAliases"));
            代码分析:此方法实现将Bean的别名保存在BaseBuilder.typeAliasRegistry中,如果别名为空,则存Bean的SimpleName。
               
          pluginElement(root.evalNode("plugins"));
            代码分析:此方法将插件信息存入configuration中,可以配置属性。configuration.interceptors

          objectFactoryElement(root.evalNode("objectFactory"));
            代码分析:对象工厂,如果为空则默认使用DefaultObjectFactory工厂。configuration.objectFactory
          objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
             代码分析:对象包装工厂,如果为空则默认使用DefaultObjectWrapperFactory。configuration.objectWrapperFactory
          settingsElement(root.evalNode("settings"));
             代码分析:先获取settings节点子节点properties属性解析到Properties props中,检查是否由不存在的setter方法,有则抛出异常。没有问题则将pros的值赋值到configuration中去,如果pros中的值为空,则设置一个默认值。
                               详细如下:
                                        configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
          configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
          configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
          configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
          configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true));
          configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
          configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
          configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
          configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
          configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
          configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
          configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
          configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
          configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
          configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
          configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
          configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
          configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
          configuration.setLogPrefix(props.getProperty("logPrefix"));
          configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));
          configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
          environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
             代码分析:设置运行环境。如果为空,默认使用default。如果设置指定的环境id,则设置事务工厂,设置数据源。并build环境。id,transactionFactory,dataSource都不允许为空。configuration.setEnvironment
          databaseIdProviderElement(root.evalNode("databaseIdProvider"));
             代码分析:获取数据库产品的名称。dataSource为空会抛出异常。注:如果使用多个数据源就需要采用多个SqlSessionFactory的配置,为不同的调用使用不同的SqlSessionFactory,在mybatis中自己是无法实现的,需要使用spring托管数据源方可。
          typeHandlerElement(root.evalNode("typeHandlers"));
             代码分析:自定义数据类型转换。提供javaType和jdbcType的映射。BaseBuilder.typeHandlerRegistry。
          mapperElement(root.evalNode("mappers"));
            代码分析:如果子节点是package,获取package的name。调用configuration.addMappers(mapperPackage);这个方法的内部调用mapperRegistry.addMappers(packageName);在这个方法中根据包名搜索下面的类并添加到knownMappers中,knownMappers是在MapperRegistry中定义的一个final map。mapper.resource使用xml资源文件。mapper.url可以使用绝对地址,理论上讲可以从网络上直接调用地址,可以在远程服务器上传一个xml拿下来解析。mapper.class使用注解方式的时候使用这种方式。
            resource和url两种配置方式都调用了XMLMapperBuilder.parse()方法,解析加载到的mapper文件,调用mapperRegistry.addMapper(Class<T> type)方法。然后移除挂起的ResultMap,挂起的缓存引用,挂起的Statement。
            通过configuration都可以获取这些mapper信息。
        
            在mapperParser.parse().configurationElement():
                
                private void configurationElement(XNode context) {
        try {
          String namespace = context.getStringAttribute("namespace");
          if (namespace.equals("")) {
           throw new BuilderException("Mapper's namespace cannot be empty");
          }
          builderAssistant.setCurrentNamespace(namespace);
          cacheRefElement(context.evalNode("cache-ref"));
          cacheElement(context.evalNode("cache"));
          parameterMapElement(context.evalNodes("/mapper/parameterMap"));
          resultMapElements(context.evalNodes("/mapper/resultMap"));
          sqlElement(context.evalNodes("/mapper/sql"));
          buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
        } catch (Exception e) {
          throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
        }
      }
                在这里解析了mapper文件,将
    二、创建SqlSessionFactory。
            在第一步加载配置问价您的时候创建了一个configuration对象,将这个对象传入SqlSessionFactoryBuilder,创建一个DefaultSqlSessionFactory的工厂对象。

    三、创建SqlSession:

            SqlSession由SqlSessionFactory创建,SqlSessionFactory提供了8种openSession多态方法,不带参数的自动提交为false,SqlSessionFactory还提供一个获取configuration的方法,调用openSession方法获取到一个DefaultSqlSession

    四、获取到Mapper代理:
            使用SqlSession.getMapper(XXXMapper.class)获取代理类,实际获取位置是MapperRegistry.knownMappers。获取到指定接口的MapperProxyFactory,然后MapperProxyFactory.newInstance(sqlSession);(这里使用了动态代理)这样就生成了一个XXXMapper的代理类,返回。
            
    五、接口方法调用:
            当调用接口方法的时候,实际上调用的MapperProxy这个类的实例对象中的invoke方法,在invoke方法中使用了方法缓存,最后调用mapperMethod.execute方法。
            下面分析SqlSession.selectOne():这个方法最终会调用到:public <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds),其中的executor根据是否开启缓存来选择,如果开启缓存:则使用CacheingExecutor.query,如果没有再调用BaseExecutor.query查询结果,并将查询出来的结果存放到CachingExecutor中的tcm(TransactionalCacheManager)对象中,TransactionalCacheManager内部使用了HashMap存储缓存和事务缓存。接着我们看BaseExecutor.query的调用,
    它最终调用到:
            private <E> List<E> queryFromDatabase(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql);
            BaseExecutor : protected abstract <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql);
            SimpleExecutor : public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException;
            经过一系列中转,最终调用到jdbc预编译,和执行,之后按照要求组装查询到的数据。
                
    六、在Mybatis中使用了的设计模式:
    1、动态代理,最典型的是通过动态代理委托MapperProxy执行相关数据操作。
    2、装饰器模式:开启缓存的时候,Caching包装了SimpleExecutor;对象工厂装饰器objectFactoryWrapper
    3、工厂模式:SqlSessionFactory的使用,还有MapperProxyFactory。
    4、构造器模式:构建Configuration对象的步骤。
    使用Mybatis相关:
    5、SqlSessionFactory无需重复创建,在使用时应该使用单例模式调用。

     
  • 相关阅读:
    rpm 命令|rpm 安装|rpm 卸载|rpm 使用|rpm 删除
    Linux中如何开启8080端口供外界访问 和开启允许对外访问的端口8000
    git远程提交到github或者gitee
    git搭建私有仓库
    Linux命令行设置环境变量
    【Little_things】控制台五子棋(java)
    【cisco实验】练习 2.3.8: 配置基本交换机管理
    操作系统FCFS,SJF进程调度(C++)
    JavaBean的编译和部署说明
    【Python爬虫】爬取个人博客的图片
  • 原文地址:https://www.cnblogs.com/liushijie/p/4712921.html
Copyright © 2011-2022 走看看