Mybatis源码分析之--配置加载

本文对mybatis的配置不做讲解,只从源码层面来分析mybatis的加载过程。

我们在使用mybatis时,一般流程如下,实例化一个sqlSessionFactory,然后通过sqlSessionFactory来获取一个sqlSession。

InputStream inputStream=null;
            try{
                
                inputStream= Resources.getResourceAsStream("mybatis-config.xml");  //配置文件
                sqlSessionFactory=new SqlSessionFactoryBuilder().build(inputStream);
                SqlSession sqlSession=sqlSessionFactory.openSession()
}catch(Exception e){ e.printStackTrace(); }

先看下sqlSessionFactory的build过程,首先将mybatis-config.xml作为一个输入流传入给build()方法,build的代码如下:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            XMLConfigBuilder e = new XMLConfigBuilder(inputStream, environment, properties); //将配置文件加载为XMLConfig
            var5 = this.build(e.parse()); //构建SqlSessionFactory
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset();

            try {
                inputStream.close();
            } catch (IOException var13) {
                ;
            }

        }

        return var5;
    }
//XMConfigBuilder中使用的XPathParser,XPathParser中使用的createDocument方法来将DOM加载到XPathParser中
private Document createDocument(InputSource inputSource) {
        try {
            DocumentBuilderFactory e = DocumentBuilderFactory.newInstance();
            e.setValidating(this.validation);
            e.setNamespaceAware(false);
            e.setIgnoringComments(true);
            e.setIgnoringElementContentWhitespace(false);
            e.setCoalescing(false);
            e.setExpandEntityReferences(true);
            DocumentBuilder builder = e.newDocumentBuilder();
            builder.setEntityResolver(this.entityResolver);
            builder.setErrorHandler(new ErrorHandler() {
                public void error(SAXParseException exception) throws SAXException {
                    throw exception;
                }

                public void fatalError(SAXParseException exception) throws SAXException {
                    throw exception;
                }

                public void warning(SAXParseException exception) throws SAXException {
                }
            });
            return builder.parse(inputSource);//将inputSource加载成Document,最后存储到XpathParser中
        } catch (Exception var4) {
            throw new BuilderException("Error creating document instance.  Cause: " + var4, var4);
        }
    }

再从最开始的var5 = this.build(e.parse())看,先看一下e.parse()方法:

public Configuration parse() {
        if(this.parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        } else {
            this.parsed = true;
            this.parseConfiguration(this.parser.evalNode("/configuration"));//解析configuration标签下的内容到Configuration对象中
            return this.configuration;
        }
    }

    private void parseConfiguration(XNode root) {
//分别解析各个标签下内容到Configuration对象中
        try {
            this.propertiesElement(root.evalNode("properties")); //properties标签
            Properties e = this.settingsAsProperties(root.evalNode("settings"));//settings标签
            this.loadCustomVfs(e);
            this.typeAliasesElement(root.evalNode("typeAliases"));
            this.pluginElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
            this.settingsElement(e);
            this.environmentsElement(root.evalNode("environments"));//environments标签
            /this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlerElement(root.evalNode("typeHandlers"));
            this.mapperElement(root.evalNode("mappers")); //Mappers对象
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

这里解析mybatis-config.xml中的所有标签出来,并加载到Configuration对象中,mybatis-config.xml的文件如下:

<configuration>

    <properties resource="jdbc.properties"/>   <!-- 属性文件,也可以直接在本文件中写properties属性 -->

    <settings>
        <setting name="cacheEnabled" value="true"/>
    </settings>

    <typeAliases>
       <package name="com.mymvc.bean"></package>
              <package name="com.java1234.model"></package>
       
    </typeAliases>
    <environments default="development">  <!-- 默认开发环境 -->
        <environment id="development">  <!-- 开发环境 -->
            <transactionManager type="JDBC" />  <!-- 事务管理器,JDBC和MANAGED(tomcat不支持) -->
            <dataSource type="POOLED">  <!-- 有UNPOOLED,POOLED,JNDI三种 -->
                <property name="driver" value="${jdbc.driverClassName}" />
                <property name="url" value="${jdbc.url}" />
                <property name="username" value="${jdbc.username}" />
                <property name="password" value="${jdbc.password}" />
            </dataSource>
        </environment>
        
        <environment id="test">  <!-- 测试环境 -->
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value="${jdbc.driverClassName}" />
                <property name="url" value="${jdbc.url}" />
                <property name="username" value="${jdbc.username}" />
                <property name="password" value="${jdbc.password}" />
            </dataSource>
        </environment>
    </environments>
    
    <mappers>
      <package name="com.java1234.mappers" />
    </mappers>

    </configuration>
View Code

这里挑一两个例子来讲解怎么加载到Configuration对象中的,首先看下settings的加载,settings的加载将内存加载成Properties类型的对象:

private Properties settingsAsProperties(XNode context) {
        if(context == null) {
            return new Properties();
        } else {
            Properties props = context.getChildrenAsProperties();//将节点转换为键值对的Properties类型,上述config.xml中toString打印即为{cacheEnabled=true}
            MetaClass metaConfig = MetaClass.forClass(Configuration.class, this.localReflectorFactory);
            Iterator var4 = props.keySet().iterator();
//metaConfig有什么用呢?
            Object key;
            do {
                if(!var4.hasNext()) {
                    return props;
                }

                key = var4.next();
            } while(metaConfig.hasSetter(String.valueOf(key)));
//while中可以看到,这里会判断settings中标签的合法性,也就是说在metaConfig中存在setter方法,才会认为是合法的标签。(必须在Configuration对象中存在相应的setter方法)

            throw new BuilderException("The setting " + key + " is not known.  Make sure you spelled it correctly (case sensitive).");
        }
    }

其他标签的加载类似,另外再讲下mappers的加载,在配置mybatis时,会经常配置到到mybatis的mapper.xml文件(sql文件映射),而且在config.xml文件中会指定对应的mappers的package在哪个地方,如:

<mappers>
      <package name="com.java1234.mappers" />
    </mappers>

具体是怎么来加载的呢?

private void mapperElement(XNode parent) throws Exception {
        if(parent != null) {
            Iterator var2 = parent.getChildren().iterator();

            while(true) {
                while(var2.hasNext()) {
                    XNode child = (XNode)var2.next();
                    String resource;
                    if("package".equals(child.getName())) { //子节点的名称为package
/**
类似
<mappers>
<package name="com.java1234.mappers" />
</mappers>
*/
                        resource = child.getStringAttribute("name");
                        this.configuration.addMappers(resource);
                    } else {
/**
<mappers>
<mapper resource="mybatis/xxx.xml" />
</mappers>

*/ resource
= child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); XMLMapperBuilder mapperParser; InputStream mapperInterface1; if(resource != null && url == null && mapperClass == null) { //resource不为空 ErrorContext.instance().resource(resource); mapperInterface1 = Resources.getResourceAsStream(resource);
//解析xml的Mapper文件 mapperParser
= new XMLMapperBuilder(mapperInterface1, this.configuration, resource, this.configuration.getSqlFragments()); mapperParser.parse(); } else if(resource == null && url != null && mapperClass == null) { //url不为空 ErrorContext.instance().resource(url); mapperInterface1 = Resources.getUrlAsStream(url); mapperParser = new XMLMapperBuilder(mapperInterface1, this.configuration, url, this.configuration.getSqlFragments()); mapperParser.parse(); } else { if(resource != null || url != null || mapperClass == null) { //mapperClass不为空 throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } Class mapperInterface = Resources.classForName(mapperClass); this.configuration.addMapper(mapperInterface); } } } return; } } }

先看下mappers标签package的方式:

                        this.configuration.addMappers(resource);
//addMappers会循环调用addMapper
public <T> void addMapper(Class<T> type) {
        if(type.isInterface()) {
            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));
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
                parser.parse();//解析
                loadCompleted = true;
            } finally {
                if(!loadCompleted) {
                    this.knownMappers.remove(type);
                }

            }
        }

    }

public void parse() {
        String resource = this.type.toString();
        if(!this.configuration.isResourceLoaded(resource)) { //判断资源是否加载
            this.loadXmlResource();
            this.configuration.addLoadedResource(resource);
            this.assistant.setCurrentNamespace(this.type.getName());
            this.parseCache();
            this.parseCacheRef();
            Method[] methods = this.type.getMethods();
            Method[] var3 = methods;
            int var4 = methods.length;

            for(int var5 = 0; var5 < var4; ++var5) {
                Method method = var3[var5];

                try {
                    if(!method.isBridge()) {
                        this.parseStatement(method);
                    }
                } catch (IncompleteElementException var8) {
                    this.configuration.addIncompleteMethod(new MethodResolver(this, method));
                }
            }
        }

        this.parsePendingMethods();
    }

private void loadXmlResource() { //加载对应的XML文件,文件名和package下的类名相同
        if(!this.configuration.isResourceLoaded("namespace:" + this.type.getName())) {
            String xmlResource = this.type.getName().replace('.', '/') + ".xml";
            InputStream inputStream = null;

            try {
                inputStream = Resources.getResourceAsStream(this.type.getClassLoader(), xmlResource);
            } catch (IOException var4) {
                ;
            }

            if(inputStream != null) {
                XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, this.assistant.getConfiguration(), xmlResource, this.configuration.getSqlFragments(), this.type.getName());
                xmlParser.parse();//xml解析
            }
        }

    }
public void parse() {
        if(!this.configuration.isResourceLoaded(this.resource)) {
            this.configurationElement(this.parser.evalNode("/mapper")); //加载mapper下的标签到Configuration
            this.configuration.addLoadedResource(this.resource);
            this.bindMapperForNamespace();
        }

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

private void configurationElement(XNode context) {
        try {
            String e = context.getStringAttribute("namespace"); //获取namespace
            if(e != null && !e.equals("")) {  //namespace不能为null或者空
                this.builderAssistant.setCurrentNamespace(e);
                this.cacheRefElement(context.evalNode("cache-ref"));
                this.cacheElement(context.evalNode("cache"));
                this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));  //parameterMap
                this.resultMapElements(context.evalNodes("/mapper/resultMap"));  //resultMapper
                this.sqlElement(context.evalNodes("/mapper/sql"));
                this.buildStatementFromContext(context.evalNodes("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);
        }
    }

这里 configurationElement 就开始在加载mapper.xml中的内容到Configuration对象,具体来看一个例子,比如cacheElement:

XML文件内容    <cache  eviction="FIFO"  flushInterval="60000"  size="512"  readOnly="true" />
private void cacheElement(XNode context) throws Exception {
        if(context != null) {
            String type = context.getStringAttribute("type", "PERPETUAL");
            Class typeClass = this.typeAliasRegistry.resolveAlias(type);
            String eviction = context.getStringAttribute("eviction", "LRU");
            Class evictionClass = this.typeAliasRegistry.resolveAlias(eviction);
            Long flushInterval = context.getLongAttribute("flushInterval");
            Integer size = context.getIntAttribute("size");
            boolean readWrite = !context.getBooleanAttribute("readOnly", Boolean.valueOf(false)).booleanValue();
            boolean blocking = context.getBooleanAttribute("blocking", Boolean.valueOf(false)).booleanValue();
            Properties props = context.getChildrenAsProperties(); //同样,也是使用Properties类型存储
            this.builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
        }

    }

其他的解析说明如下,源码不一一粘贴了:


  • cacheRefElement方法用于解析<cache-ref>标签,总结如下:
  1. 解析完的CacheRef放在cacheRefMap中
  2. cacheRefMap是一个HashMap
  3. 位于Configuration对象中
  4. Key为mapper文件的namespace,Value为<cache-ref>中配置的namespace
  • cacheElement方法用于解析<cache>标签,总结如下:
  1. 会根据<cache>中配置的属性new出一个org.apache.ibatis.cache.Cache
  2. 使用此Cache作为MyBatis缓存
  • parameterMapElement方法用于解析<parameterMap>标签,总结如下:
  1. 解析完的ParameterMap放在parameterMaps中
  2. parameterMaps是一个StrictMap
  3. 位于Configuration对象中,StrictMap是HashMap的子类
  4. Key为当前mapper的namespace+"."+<parameterMap>标签中的id属性,Value为ParameterMap对象
  • resultMapElements方法用于解析<resultMap>标签在,总结如下:
  1. 解析完的ResultMap放在resultMaps中
  2. resultMaps是一个StrictMap,
  3. 位于Configuration对象中
  4. Key为当前mapper的namespace+"."+<resultMap>标签中的id属性,Value为ResultMap对象
  • sqlElement方法用于解析<sql>标签,总结如下:
  1. 解析完的内容放在sqlFragments中
  2. sqlFragments是一个StrictMap
  3. 位于XMLMapperBuilder对象中
  4. Key为当前mapper的namespace+"."+<sql>标签中的id属性,Value为sql这个XNode本身
  • buildStatementFromContext用于解析<select>、<insert>、<update>、<delete>这四个标签,总结如下:
    1. 解析完的内容放在mappedStatements中
    2. mappedStatements是一个StrictMap
    3. 位于Configuration对象中
    4. Key为当前mapper的namespace+"."+<select>|<insert>|<update>|<delete>标签中的id属性,Value为MappedStatement对象

最后一步,就是构建sqlSessionFactory了,将加载的config对象传入

    public SqlSessionFactory build(Configuration config) {
        return new DefaultSqlSessionFactory(config);
    }

最终构建出来的SqlSessionFactory是DefaultSqlSessionFactory

接下来会通过openSession()方法获取到一个sqlSession

//通过openSession()方法,传入的参数默认为this.configuration.getDefaultExecutorType(),null,false
//其中defaultExecutor为Simple,可追踪源码看到Configuration的构造函数中的初始化
//this.defaultExecutorType = ExecutorType.SIMPLE;

private
SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) { Transaction tx = null; DefaultSqlSession var8; try { Environment e = this.configuration.getEnvironment(); //获取配置的environment TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(e); //获取事务工厂 tx = transactionFactory.newTransaction(e.getDataSource(), level, autoCommit); //获取事务,传入参数为DataSource,事务隔离级别,是否自动提交,可根据之前的config.xml中的environment中的参数一一对应,即事务为JDBC类型,DataSource为Pooled
Executor executor
= this.configuration.newExecutor(tx, execType); //获取一个executor var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);//new一个defaultSqlSession } catch (Exception var12) { this.closeTransaction(tx); throw ExceptionFactory.wrapException("Error opening session. Cause: " + var12, var12); } finally { ErrorContext.instance().reset(); } return var8; }

看下executor的获取:

//executor有三种,batch,simple,reuse
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
        executorType = executorType == null?this.defaultExecutorType:executorType;
        executorType = executorType == null?ExecutorType.SIMPLE:executorType;
        Object executor;
        if(ExecutorType.BATCH == executorType) {
            executor = new BatchExecutor(this, transaction);
        } else if(ExecutorType.REUSE == executorType) {
            executor = new ReuseExecutor(this, transaction);
        } else {
            executor = new SimpleExecutor(this, transaction);
        }

        if(this.cacheEnabled) {//装饰器模式,传入executor,返回给simpleExecutor装上了换成功能
            executor = new CachingExecutor((Executor)executor);
        }

        Executor executor1 = (Executor)this.interceptorChain.pluginAll(executor); //加载插件
        return executor1;
    }

到这里,sqlSession就获取完了。

posted on 2017-10-17 18:10  qiezijiajia 阅读( ...) 评论( ...) 编辑 收藏

转载于:https://www.cnblogs.com/dpains/p/7682869.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值