五Mybatis 核心对象之 Configuration 对象初始化详解
Posted archerLuo罗
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了五Mybatis 核心对象之 Configuration 对象初始化详解相关的知识,希望对你有一定的参考价值。
- 源码地址:https://github.com/RononoaZoro/mybatis-book/tree/master 的 mybatis-book ( mybatis-chapter04 )
- 文章内容出自《Mybatis 3 源码深度解析》第四章
- 自己实现代码地址:https://github.com/RononoaZoro/mybatis-book/tree/master 的 archer-mybatis
1、说明
- 本文以 XML 格式配置文件为例子,详解 Configuration 的初始化过程
- 其他配置文件类型(如:注解形式,yml, properties 等)请按照相同思路参考源码
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<settings>
<setting name="useGeneratedKeys" value="true"/>
<setting name="mapUnderscoreToCamelCase" value="true"/>
<setting name="logImpl" value="LOG4J"/>
</settings>
<environments default="dev" >
<environment id="dev">
<transactionManager type="JDBC">
<property name="" value="" />
</transactionManager>
<dataSource type="UNPOOLED">
<property name="driver" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:mybatis" />
<property name="username" value="sa" />
<property name="password" value="" />
</dataSource>
</environment>
<environment id="qa">
<transactionManager type="JDBC">
<property name="" value="" />
</transactionManager>
<dataSource type="UNPOOLED">
<property name="driver" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:mybatis_qa" />
<property name="username" value="admin" />
<property name="password" value="admin" />
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="mapper/UserMapper.xml"/>
<!--
<mapper resource="file:///mybatis/com/blog4java/mybatis/example/mapper/UserMapper.xml"/>
<mapper class="com.blog4java.mybatis.com.blog4java.mybatis.example.mapper.UserMapper"/>
<package name="com.blog4java.mybatis.com.blog4java.mybatis.example.mapper"/>
-->
</mappers>
</configuration>
log4j.properties
log4j.rootLogger=INFO, stdout
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.Threshold=INFO
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d %p [%c,%L] - %m%n
UserEntity.java
package com.luo.example.entity;
import lombok.Data;
import java.util.Date;
@Data
public class UserEntity {
private Long id;
private String name;
private Date createTime;
private String password;
private String phone;
private String nickName;
}
UserMappe.javar
package com.luo.example.mapper;
import com.luo.example.entity.UserEntity;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper {
List<UserEntity> listAllUser();
// @Select("select * from user where id=#{userId,jdbcType=INTEGER}")
UserEntity getUserById(@Param("userId") String userId);
List<UserEntity> getUserByEntity(UserEntity user);
UserEntity getUserByPhone(@Param("phone") String phone);
}
2、Configuration 初始化入口
package com.blog4java.mybatis.configuration;
import org.apache.ibatis.builder.xml.XMLConfigBuilder;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.Configuration;
import org.junit.Test;
import java.io.IOException;
import java.io.Reader;
public class ConfigurationExample {
@Test
public void testConfiguration() throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
// 创建XMLConfigBuilder实例
XMLConfigBuilder builder = new XMLConfigBuilder(reader);
// 调用XMLConfigBuilder.parse()方法,解析XML创建Configuration对象
Configuration conf = builder.parse();
}
}
3、Configuration 初始化详细步骤解析
3.1、将配置文件读取到 IO 流
//详细读取过程,请参考 Java 官方有关流的 API
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
3.2、创建解析 XML 文件所需相关对象
3.2.1、创建 XMLConfigBuilder 对象
// 创建XMLConfigBuilder实例
XMLConfigBuilder builder = new XMLConfigBuilder(reader);
3.2.2、XMLConfigBuilder 的初始化源码解析
- 1、创建了用于解析 XML 文件的对象(如:XPathParser, XMLMapperEntityResolve, Document 等)
- 2、创建 Configuration 空对象
- 3、说明:
- 以下是部分 XMLConfigBuilder 核心代码,完整代码请参考源码
- BaseBuilder 定义了各种配置类型解析 Builder 对象的通用属性和方法
XMLConfigBuilder
public class XMLConfigBuilder extends BaseBuilder {
private boolean parsed;
private final XPathParser parser;
public XMLConfigBuilder(Reader reader) {
this(reader, null, null);
}
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
}
BaseBuilder
public abstract class BaseBuilder {
protected final Configuration configuration;
protected final TypeAliasRegistry typeAliasRegistry;
protected final TypeHandlerRegistry typeHandlerRegistry;
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
}
3.2.3、解析 mybatis-config.xml ,保存到 Configuration 对象中
- 完整解析过程参考源码
//调用XMLConfigBuilder.parse()方法,解析XML 保存到 Configuration 对象
Configuration conf = builder.parse();
public Configuration parse() {
// 防止parse()方法被同一个实例多次调用
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 调用XPathParser.evalNode()方法,创建表示configuration节点的XNode对象。
// 调用parseConfiguration()方法对XNode进行处理
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
//数据源解析,并创建 DataSource 对象
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
//Mapper 接口与 Mapper.xml 解析,并建立起绑定关系
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
4、自己实现简化版 Mybatis 的 Configuration
4.1、测试入口
package com.luo.example;
import com.luo.ibatis.builder.xml.ArcherXMLConfigBuilder;
import com.luo.ibatis.session.ArcherConfiguration;
import org.apache.ibatis.io.Resources;
import org.junit.Test;
import java.io.IOException;
import java.io.Reader;
public class ConfigurationExample {
@Test
public void testConfiguration() throws IOException {
Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
// 创建XMLConfigBuilder实例
ArcherXMLConfigBuilder builder = new ArcherXMLConfigBuilder(reader);
// 调用XMLConfigBuilder.parse()方法,解析XML创建Configuration对象
ArcherConfiguration conf = builder.parse();
}
}
4.2、ArcherXMLConfigBuilder 类(对应 XMLConfigBuilder)
package com.luo.ibatis.builder.xml;
import com.luo.ibatis.builder.ArcherBaseBuilder;
import com.luo.ibatis.session.ArcherConfiguration;
import org.apache.ibatis.builder.BuilderException;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.builder.xml.XMLMapperEntityResolver;
import org.apache.ibatis.datasource.DataSourceFactory;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.logging.Log;
import org.apache.ibatis.mapping.Environment;
import org.apache.ibatis.parsing.XNode;
import org.apache.ibatis.parsing.XPathParser;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaClass;
import org.apache.ibatis.reflection.ReflectorFactory;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.transaction.TransactionFactory;
import org.apache.ibatis.type.JdbcType;
import javax.sql.DataSource;
import java.io.InputStream;
import java.io.Reader;
import java.util.Properties;
public class ArcherXMLConfigBuilder extends ArcherBaseBuilder {
private boolean parsed;
private final XPathParser parser;
private String environment;
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
public ArcherXMLConfigBuilder(Reader reader) {
this(reader, null, null);
}
public ArcherXMLConfigBuilder(Reader reader, String environment) {
this(reader, environment, null);
}
public ArcherXMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
public ArcherXMLConfigBuilder(InputStream inputStream) {
this(inputStream, null, null);
}
public ArcherXMLConfigBuilder(InputStream inputStream, String environment) {
this(inputStream, environment, null);
}
public ArcherXMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
private ArcherXMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new ArcherConfiguration());
ErrorContext.instance().resource("SQL Mapper Configuration");
// this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
public ArcherConfiguration parse() {
// 防止parse()方法被同一个实例多次调用
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
// 调用XPathParser.evalNode()方法,创建表示configuration节点的XNode对象。
// 调用parseConfiguration()方法对XNode进行处理
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
// issue #117 read properties first
//properties 节点解析
// propertiesElement(root.evalNode("properties"));
//settings 节点解析为 Properties 对象
Properties settings = settingsAsProperties(root.evalNode("settings"));
//接入虚拟文件系统
// loadCustomVfs(settings);
//别名解析
// typeAliasesElement(root.evalNode("typeAliases"));
//插件解析
// pluginElement(root.evalNode("plugins"));
//对象工厂
// objectFactoryElement(root.evalNode("objectFactory"));
//对象工厂装饰类
// objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
//反射
// reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 将 settings 节点里的详细保存到 Configuration 对象中
settingsElement(settings);
// // read it after objectFactory and objectWrapperFactory issue #631
//environments 节点解析 数据源信息相关,创建 DataSource 对象
environmentsElement(root.evalNode("environments"));
// 数据库厂商表示节点 解析
// databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//typeHandler 节点解析
// typeHandlerElement(root.evalNode("typeHandlers"));
//mapper节点 文件解析
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
private Properties settingsAsProperties(XNode context) {
if (context == null) {
return new Properties();
}
Properties props = context.getChildrenAsProperties();
// Check that all settings are known to the configuration class
MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory);
for (Object key : props.keySet()) {
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
return props;
}
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
以上是关于五Mybatis 核心对象之 Configuration 对象初始化详解的主要内容,如果未能解决你的问题,请参考以下文章