mybatis 初始化过程
调用时序图
mybatis首先是加载 mybatis-config.xml 文件
读取mybatis-config.xml
Reader reader = Resources.getResourceAsReader("org/apache/ibatis/autoconstructor/mybatis-config.xml")
根据mybatis-config.xml, 创建SqlSessionFactory
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
通过调用 SqlSessionFactoryBuilder 的 build方法,把读取的 mybatis-config.xml 文件带入
在 SqlSessionFactoryBuilder 中通过 build 方法的重载,调用真正的 build 方法
创建 Configuration 对象,Configuration 是 mybatis 中的全局配置
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
在 XMLConfigBuilder 中,又是通过重载的方式, 使用 this 调用了构造
真正的创建 Configuration 对象
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
// 创建 Configuration 对象, 并在父类 BaseBuilder 中赋值
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
然后返回到 SqlSessionFacotryBuilder ,查看返回的 XMLConfiguration 对象调用的 parse()
判断是否加载过 mybatis-config.xml 的配置,然后解析 mybatis-config.xml 中 中的属性
mybatis-config.xml 中有 数据源,mapper信息, 插件,以及别名,自定义类型,配置 等等
private void parseConfiguration(XNode root) {
try {
// 解析 *.properties 的文件,添加到 config
propertiesElement(root.evalNode("properties"));
// 解析 setting 表中的数据,并进行配置
Properties settings = settingsAsProperties(root.evalNode("settings"));
// 解析 settings 中的 vfsImpl 标签
// vfs 是一个虚拟文件系统
loadCustomVfs(settings);
// 解析 logImpl 可指定对应的日志实现
loadCustomLogImpl(settings);
// 解析 别名设置
// <typeAlias type="com.demo.entity.User" alias="user"/>
typeAliasesElement(root.evalNode("typeAliases"));
// 解析 插件标签
pluginElement(root.evalNode("plugins"));
// 解析 对象工厂
// <objectFactory type="com.demo.config.ExampleObjectFactory">
// 通过获取标签的 type 获取 class 包路径,然后通过反射创建对象,
// 并放到 configuration中
objectFactoryElement(root.evalNode("objectFactory"));
// 解析 对象包装器工厂
// 同理根据包路径反射创建对象,并放到 configuration
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 解析 自定义反射工厂
// 也是根据获取包路径,然后反射创建对象,放到 configuration
reflectorFactoryElement(root.evalNode("reflectorFactory"));
// 把 setting 中的属性赋值到 configuration中
settingsElement(settings);
// 解析配置文件中的数据源,并设置到 Configuration 的 Environment 中
environmentsElement(root.evalNode("environments"));
// 多数据库配置类
// 比如同一个服务中同时使用 mysql oracle
// 在 xml 上添加 databaseId 即可使用指定的数据方言
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 解析自定义的数据类型
typeHandlerElement(root.evalNode("typeHandlers"));
// 解析 mapper.xml
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
// 解析 typeAliases 标签
private void typeAliasesElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
// 获取别名
String alias = child.getStringAttribute("alias");
// 获取文件路径
String type = child.getStringAttribute("type");
try {
// 反射创建 class
Class<?> clazz = Resources.classForName(type);
// 注册到 typeAliasRegistry 中
if (alias == null) {
typeAliasRegistry.registerAlias(clazz);
} else {
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
// TypeAliasRegistry 通过一个 Map<String, Class<?>> 进行存储 alias
// 构造方法中加载了常用的数据类型
public TypeAliasRegistry() {
registerAlias("string", String.class);
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("byte[]", Byte[].class);
registerAlias("long[]", Long[].class);
registerAlias("short[]", Short[].class);
registerAlias("int[]", Integer[].class);
registerAlias("ResultSet", ResultSet.class);
...
}
// 解析插件
// <plugin interceptor="com.demo.pligins.ExamplePlugin"></plugin>
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
// 获取 plugins 集合
for (XNode child : parent.getChildren()) {
// 获取 plugin 标签中的 interceptor
// class 的包路径
String interceptor = child.getStringAttribute("interceptor");
Properties properties = child.getChildrenAsProperties();
// 通过反射创建插件
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance();
interceptorInstance.setProperties(properties);
// 添加到全局配置(Configuration)中
configuration.addInterceptor(interceptorInstance);
}
}
}
// 插件处理类
public class InterceptorChain {
// 把所有的插件全部放到 list 中
private final List<Interceptor> interceptors = new ArrayList<>();
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
// 根据插件生成对应的代理类
// Interceptor 接口中实现
target = interceptor.plugin(target);
}
return target;
}
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}
// 插件统一实现类
public interface Interceptor {
Object intercept(Invocation invocation) throws Throwable;
default Object plugin(Object target) {
// 生成代理类的方法
return Plugin.wrap(target, this);
}
default void setProperties(Properties properties) {
// NOP
}
}
// 插件处理类
public class Plugin implements InvocationHandler {
public static Object wrap(Object target, Interceptor interceptor) {
Map<Class<?>, Set<Method>> signatureMap = getSignatureMap(interceptor);
Class<?> type = target.getClass();
Class<?>[] interfaces = getAllInterfaces(type, signatureMap);
if (interfaces.length > 0) {
// 通过 jdk 动态代理生成 代理类
return Proxy.newProxyInstance(
type.getClassLoader(),
interfaces,
new Plugin(target, interceptor, signatureMap));
}
return target;
}
}
// mybatis-config.xml 中 settings 标签中设置的属性, 如果属性不存在就是用默认值
private void settingsElement(Properties props) {
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE")));
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"), false));
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.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), null));
configuration.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType")));
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.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler")));
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true));
configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false));
configuration.setLogPrefix(props.getProperty("logPrefix"));
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
configuration.setShrinkWhitespacesInSql(booleanValueOf(props.getProperty("shrinkWhitespacesInSql"), false));
configuration.setDefaultSqlProviderType(resolveClass(props.getProperty("defaultSqlProviderType")));
}
// 解析数据源信息赋值到 configuration 的 environment 中
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
// 数据源id
String id = child.getStringAttribute("id");
if (isSpecifiedEnvironment(id)) {
// 事务管理器 一个 JDBC,一个MANAGED
// JDBC 是直接使用 jdbc 中的 commit 和 rollback
// MANAGED 让容器实现事务,mybatis 不会进行实现
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// 数据源信息
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
configuration.setEnvironment(environmentBuilder.build());
break;
}
}
}
}
// 数据库配置类
public final class Environment {
private final String id;
private final TransactionFactory transactionFactory;
private final DataSource dataSource;
}
// 解析自定义数据类型
private void typeHandlerElement(XNode parent) {
if (parent != null) {
for (XNode child : parent.getChildren()) {
// 如果是使用 package 那么将包下的所有 class 都注册到 configuration 的 typeHandlerRegistry 中
if ("package".equals(child.getName())) {
String typeHandlerPackage = child.getStringAttribute("name");
// 调用注册方法
typeHandlerRegistry.register(typeHandlerPackage);
} else {
// 解析判断是什么类型,最后注册到 configuration 的 typeHandlerRegistry 中
String javaTypeName = child.getStringAttribute("javaType");
String jdbcTypeName = child.getStringAttribute("jdbcType");
String handlerTypeName = child.getStringAttribute("handler");
Class<?> javaTypeClass = resolveClass(javaTypeName);
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
if (javaTypeClass != null) {
if (jdbcType == null) {
// 调用注册方法
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
// 调用注册方法
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
// 调用注册方法
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
// 解析 *mapper.xml 文件
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
// 如果传入一个包路径, 根据包路径解析包路径下的所有xml
// 并将所有反射创建出来的mapper保存在 configuration 的 mapperRegistry 中
if ("package".equals(child.getName())) {
String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);
} else {
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
// 通过 resource 解析
ErrorContext.instance().resource(resource);
// 根据 xml 路径(com/demo/resource/*mapper.xml)读取 xml
try(InputStream inputStream = Resources.getResourceAsStream(resource)) {
// 根据 xml 创建 mapper对象
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
// 解析 mapper.xml 中的标签
mapperParser.parse();
}
} else if (resource == null && url != null && mapperClass == null) {
// 通过 url 解析, 跟 resource 方式相同
ErrorContext.instance().resource(url);
try(InputStream inputStream = Resources.getUrlAsStream(url)){
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());
mapperParser.parse();
}
} else if (resource == null && url == null && mapperClass != null) {
Class<?> mapperInterface = Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);
} else {
throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
}
}
}
}
}
// 存放解析出来的 xml 文件
// 通过 class 对应 xml 解析后的代理对象的方式存储 xml信息
public class MapperRegistry {
private final Configuration config;
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
}
// 解析 mapper.xml 中的标签
public void parse() {
// 判断当前 mapper.xml 是否已经加载过
if (!configuration.isResourceLoaded(resource)) {
// 解析 mapper.xml 中的标签
configurationElement(parser.evalNode("/mapper"));
// 将 mapper.xml 的包路径存在 configuration 中的 loadResouece 中
// loadResource 是一个 Set
configuration.addLoadedResource(resource);
// 绑定类型到 nameSpace上
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
// 解析 mapper.xml 下的标签
private void configurationElement(XNode context) {
try {
// 获取 xml 中的 nameSpace
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.isEmpty()) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
// 将 nameSpace 放到 MapperBuilderAssistant 对象中
builderAssistant.setCurrentNamespace(namespace);
// 解析引用缓存
cacheRefElement(context.evalNode("cache-ref"));
// 解析缓存
cacheElement(context.evalNode("cache"));
// 解析 parameterMap 标签
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
// 解析 resultMap 标签
resultMapElements(context.evalNodes("/mapper/resultMap"));
// 解析 sql 标签
sqlElement(context.evalNodes("/mapper/sql"));
// 解析 select insert update delete 标签
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
// 解析 insert select update delete 标签
private void buildStatementFromContext(List<XNode> list) {
// 判断是否使用了多数据库
if (configuration.getDatabaseId() != null) {
// 如果使用多数据库,将指定数据库id(oracle | mysql)
buildStatementFromContext(list, configuration.getDatabaseId());
}
// 单方言解析 sql
buildStatementFromContext(list, null);
}
// 处理 sql 语句标签
private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {
// 循环所有的sql标签
for (XNode context : list) {
// 构建 sql Statement 对象
final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);
try {
// 解析 sql
statementParser.parseStatementNode();
} catch (IncompleteElementException e) {
// 存放解析错误的 xml
configuration.addIncompleteStatement(statementParser);
}
}
}
// 解析所有 sql 中的属性
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// Include Fragments before parsing
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String resultType = context.getStringAttribute("resultType");
Class<?> resultTypeClass = resolveClass(resultType);
String resultMap = context.getStringAttribute("resultMap");
String resultSetType = context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
if (resultSetTypeEnum == null) {
resultSetTypeEnum = configuration.getDefaultResultSetType();
}
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");
// 存储解析后的sql
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
// 将解析后的sql对象 MappedStatement 对象 存入 configuration
public MappedStatement addMappedStatement(
String id,
SqlSource sqlSource,
StatementType statementType,
SqlCommandType sqlCommandType,
Integer fetchSize,
Integer timeout,
String parameterMap,
Class<?> parameterType,
String resultMap,
Class<?> resultType,
ResultSetType resultSetType,
boolean flushCache,
boolean useCache,
boolean resultOrdered,
KeyGenerator keyGenerator,
String keyProperty,
String keyColumn,
String databaseId,
LanguageDriver lang,
String resultSets) {
if (unresolvedCacheRef) {
throw new IncompleteElementException("Cache-ref not yet resolved");
}
id = applyCurrentNamespace(id, false);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)
.resource(resource)
.fetchSize(fetchSize)
.timeout(timeout)
.statementType(statementType)
.keyGenerator(keyGenerator)
.keyProperty(keyProperty)
.keyColumn(keyColumn)
.databaseId(databaseId)
.lang(lang)
.resultOrdered(resultOrdered)
.resultSets(resultSets)
.resultMaps(getStatementResultMaps(resultMap, resultType, id))
.resultSetType(resultSetType)
.flushCacheRequired(valueOrDefault(flushCache, !isSelect))
.useCache(valueOrDefault(useCache, isSelect))
.cache(currentCache);
ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);
if (statementParameterMap != null) {
statementBuilder.parameterMap(statementParameterMap);
}
MappedStatement statement = statementBuilder.build();
configuration.addMappedStatement(statement);
// 在回到 parae() 中
return statement;
}
处理完 mybatis-config.xml 中的标签后返回 configuration
最后 build(configuration)
创建全局的 DefaultSqlSessionFactory