mybatis运行原理(一)

我们平常使用mybatis如下,主要分为四步:





<configuration>
     <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC" />
            <dataSource type="POOLED">
                <property name="driver" value=driver />
                <property name="url" value=url />
                <property name="username" value=username />
                <property name="password" value=password />
            dataSource>
        environment>
    environments>

    <mappers>
        <mapper resource="mapper.xml"/>
    mappers>
configuration>

以及若干mapper.xml和接口

public static void main(String[] args) {
	InputStream is = Test.class.getClassLoader().getResourceAsStream("mybatis-config.xml");
	//1、得到SqlSessionFactory 
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);
	//2、得到SqlSession 
	SqlSession session = factory.openSession();
	//3、得到mapper 
	StuInfoMapper mapper = session.getMapper(StuInfoMapper.class);
	//4、执行得到结果
	StuInfo stu = mapper.getStuById("12345678");
	System.out.println(stu);
	session.close();
    }

对于mybatis不太熟悉的可以和mybatis官方文档一起看看

一、生成SqlSessionFactory

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

new一个SqlSessionFactoryBuilder,调用build方法,实际是做了这些操作:

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
    	//定义一个XMLConfigBuilder(基于XPathParser)
        XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
        //使用parse解析,将解析结果给了build,再次调用build,生成SqlSessionFactory并返回
        var5 = this.build(parser.parse());
		
        ...//此处省略若干
        
        return var5;
    }
    
public SqlSessionFactory build(Configuration config) {
	//生成一个DefaultSqlSessionFactory返回
	return new DefaultSqlSessionFactory(config);
}

总结一下这个过程:
1、 定义一个XMLConfigBuilder解析配置文件(mybatis-config.xml),解析之后生成一个Configuration ,存放配置文件中的全部信息。
2、把配置文件给默认的SqlSessionFactory,返回生成的DefaultSqlSessionFactory

核心是解析配置文件

二、解析配置文件

public Configuration parse() {
	//省略若干代码
	//将文件中的configuration标签的内容拿出来作为node
	//调用parseConfiguration解析这个node的内容
	this.parseConfiguration(this.parser.evalNode("/configuration"));
}

对configuration标签记不清楚的可以回到最开始看一下配置文件,这个标签包含了配置文件全部的内容。
configuration标签下可以配置properties,settings等标签(必须按顺序配置)。
按顺序将每一个标签取出作为node,存入不同的Element进行处理

private void parseConfiguration(XNode root) {
        try {
            this.propertiesElement(root.evalNode("properties"));
            Properties settings = this.settingsAsProperties(root.evalNode("settings"));
            //加载vfs
            this.loadCustomVfs(settings);
            //加载日志
            this.loadCustomLogImpl(settings);
            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"));
            //设置set标签的各个属性的值(默认值|用户定义的值)
            this.settingsElement(settings);
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlerElement(root.evalNode("typeHandlers"));
            //我们常用的mapper标签
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }

三、处理mappers标签

常用的就是mapppers标签,所以专门说一下。

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

        while(true) {
        	//遍历下面的每一个mapper标签
            while(var2.hasNext()) {
                XNode child = (XNode)var2.next();
                String resource;
                //如果是package属性,则configuration.addMappers
                if ("package".equals(child.getName())) {
                    resource = child.getStringAttribute("name");
                    this.configuration.addMappers(resource);
                } else {
                	//如果是resource或者url 属性,则用XMLMapperBuilder(基于XPathParser)对对应的mapper.xml文件进行解析
                    resource = child.getStringAttribute("resource");
                    String url = child.getStringAttribute("url");
                    String mapperClass = child.getStringAttribute("class");
                    XMLMapperBuilder mapperParser;
                    InputStream inputStream;
                    if (resource != null && url == null && mapperClass == null) {
                        ErrorContext.instance().resource(resource);
                        inputStream = Resources.getResourceAsStream(resource);
                        mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                        mapperParser.parse();
                    } else if (resource == null && url != null && mapperClass == null) {
                        ErrorContext.instance().resource(url);
                        inputStream = Resources.getUrlAsStream(url);
                        mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
                        mapperParser.parse();
                    } else {
                        if (resource != null || url != null || mapperClass == null) {
                            throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                        }
						//如果是mapperClass(接口),则也直接添加configuration.addMappers
                        Class<?> mapperInterface = Resources.classForName(mapperClass);
                        this.configuration.addMapper(mapperInterface);
                    }
                }
            }
            return;
        }
    }
}

1、package属性或者mapperClass(接口),则添加->configuration.addMappers
2、resource或者url 属性,则用XMLMapperBuilder进行解析

1、添加

public void addMappers(String packageName) {
   this.mapperRegistry.addMappers(packageName);
}
   
//经过一系列的操作,将string的包名,变为Class type
public <T> void addMapper(Class<T> type) {
    this.mapperRegistry.addMapper(type);
}

//然后添加
public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
        //省略若干代码
        //给configuration的mapperRegistry的knownMappers存入{接口,MapperProxyFactory}键值对
        this.knownMappers.put(type, new MapperProxyFactory(type));
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
        parser.parse();
        loadCompleted = true;
    }
}

2、解析mapper.xml

public void parse() {
   if (!this.configuration.isResourceLoaded(this.resource)) {
   		//根据全部标签,设置不同的Element,与上面解析配置文件相同
        this.configurationElement(this.parser.evalNode("/mapper"));
        //给configuration中添加
        this.configuration.addLoadedResource(this.resource);
        //将接口与xml文件绑定,并添加进configuration
        this.bindMapperForNamespace();
    }
	//解析ResultMap
    this.parsePendingResultMaps();
    //解析缓存引用
    this.parsePendingCacheRefs();
    //解析sql语句
    this.parsePendingStatements();
}

//将接口与xml文件绑定,并添加进configuration
private void bindMapperForNamespace() {
    String namespace = this.builderAssistant.getCurrentNamespace();
    if (namespace != null) {
        Class boundType = null;

        try {
            boundType = Resources.classForName(namespace);
        } catch (ClassNotFoundException var4) {
        }

        if (boundType != null && !this.configuration.hasMapper(boundType)) {
            this.configuration.addLoadedResource("namespace:" + namespace);
            //最后还是进行了configuration的添加
            this.configuration.addMapper(boundType);
        }
    }
}

//解析mapper.xml的全部标签
private void configurationElement(XNode context) {
    try {
        String namespace = context.getStringAttribute("namespace");
        if (namespace != null && !namespace.equals("")) {
            this.builderAssistant.setCurrentNamespace(namespace);
            this.cacheRefElement(context.evalNode("cache-ref"));
            this.cacheElement(context.evalNode("cache"));
            this.parameterMapElement(context.evalNodes("/mapper/parameterMap"));
            this.resultMapElements(context.evalNodes("/mapper/resultMap"));
            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. The XML location is '" + this.resource + "'. Cause: " + var3, var3);
    }
}

3、处理增删改查标签

//list全部的增删改查标签
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
    Iterator var3 = list.iterator();

    while(var3.hasNext()) {
        XNode context = (XNode)var3.next();
        //用XMLStatementBuilder 解析每一个标签
        XMLStatementBuilder statementParser = new XMLStatementBuilder(this.configuration, this.builderAssistant, context, requiredDatabaseId);

        try {
            statementParser.parseStatementNode();
        } catch (IncompleteElementException var7) {
            this.configuration.addIncompleteStatement(statementParser);
        }
    }
}

public void parseStatementNode() {
    String id = this.context.getStringAttribute("id");
    String databaseId = this.context.getStringAttribute("databaseId");
    if (this.databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
        Integer fetchSize = this.context.getIntAttribute("fetchSize");
       	String parameterMap = this.context.getStringAttribute("parameterMap");
        //获取全部属性,timeout,parameterType,resultMap,lang等
        //根据获得的属性值,对一些内容进行相应设置
        //此处已省略

		//将获得的全部属性值添加
        this.builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType, fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass, resultSetTypeEnum, flushCache, useCache, resultOrdered, (KeyGenerator)keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
    }
}

//addMappedStatement方法
/*
	最终会生成MappedStatement ,并将其存入configuration
	MappedStatement statement = statementBuilder.build();
    this.configuration.addMappedStatement(statement);
*/

当处理完配置文件的全部标签、属性,以及定义的mapper.xml的全部内容后,就结束了,这个时候我们看一下configuration

首先它存放了非常多的信息
mybatis运行原理(一)_第1张图片
其中有一个这个,这是我们之前存进去的接口,以后会用到很重要
mybatis运行原理(一)_第2张图片
开始第二步,得到sqlsession

你可能感兴趣的:(源码分析)