在不整合spring的情況下,通過mybatis的xml配置問你件mybatis的各種屬性,完成SqlSessionFactory初始化。
public SqlSessionFactory getSqlSessionFactory() throws IOException {
return new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("mybatis-config.xml"));
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//通過XMLConfigBuilder對象 解析 xml配置檔案
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
//父類的configuration屬性指派為new Configuration() , configuration對象很重要 , 裡面儲存非常多的屬性。
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
//configuration的的variables設定為props
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
//parser指派為parser
this.parser = parser;
}
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
//從根标簽configuration開始解析
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//解析properties标簽 比如<properties resource="database.properties"/> 引入配置資訊
//使得可以在xml配置檔案中使用${}占位符擷取相應的值
propertiesElement(root.evalNode("properties"));
//解析settings标簽 擷取子标簽setting的name value屬性,封裝為Properties對象傳回
//<setting name="safeResultHandlerEnabled" value="true"/> 開啟自動駝峰轉換
//<setting name="cacheEnabled" value="true"/> 開啟二級緩存功能 (一般不去使用,特别是分布式的環境)
Properties settings = settingsAsProperties(root.evalNode("settings"));
//擷取vfsImpl屬性值,設定到configuration的vfsImpl屬性中
//且再VFS類的USER_IMPLEMENTATIONS容器中放入該vfsImpl對應的class對象
//一般使用預設的DefaultVFS
loadCustomVfs(settings);
//擷取到logImpl的屬性值,設定到configuration的logImpl屬性中
//且把執行個體化該屬性值對應的類的構造函數,把該日志構造函數設定到LogFactory的logConstructor屬性中
loadCustomLogImpl(settings);
//解析typeAliases标簽
typeAliasesElement(root.evalNode("typeAliases"));
//解析plugins标簽
pluginElement(root.evalNode("plugins"));
//這幾個屬性一般使用預設的就行,資料庫字段映射到實體類用到的類
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
//将setting标簽配置的各種屬性封裝到configuration對應的屬性中
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
//解析environments标簽
environmentsElement(root.evalNode("environments"));
//解析databaseIdProvider标簽
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
//解析typeHandlers标簽
typeHandlerElement(root.evalNode("typeHandlers"));
//解析mappers标簽
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
解析properties标簽
private void propertiesElement(XNode context) throws Exception {
if (context != null) {
//擷取子标簽的name value屬性,封裝為Properties對象
Properties defaults = context.getChildrenAsProperties();
//擷取resource屬性的值
String resource = context.getStringAttribute("resource");
//擷取url屬性的值
String url = context.getStringAttribute("url");
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
//讀取配置檔案的值,封裝為Properties對象 加入到defaults 中。
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
}
//從configuration中擷取variables
Properties vars = configuration.getVariables();
if (vars != null) {
//如果有值,添加到defaults中
defaults.putAll(vars);
}
//将parser的variables設定為defaults
parser.setVariables(defaults);
//将configuration的variables設定為defaults
configuration.setVariables(defaults);
}
}
解析name屬性為logImpl的setting标簽
private void loadCustomLogImpl(Properties props) {
//擷取logImpl屬性值,并且包裝為class對象,這裡的值可以不是一個類的完整限定名,而是一個别名
Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl"));
//将logImpl設定到configuration的logImpl屬性中
configuration.setLogImpl(logImpl);
}
protected <T> Class<? extends T> resolveClass(String alias) {
if (alias == null) {
return null;
}
try {
//看這裡
return resolveAlias(alias);
} catch (Exception e) {
throw new BuilderException("Error resolving class. Cause: " + e, e);
}
}
protected <T> Class<? extends T> resolveAlias(String alias) {
//看這裡
return typeAliasRegistry.resolveAlias(alias);
}
public <T> Class<T> resolveAlias(String string) {
try {
if (string == null) {
return null;
}
// issue #748
//先轉為小寫
String key = string.toLowerCase(Locale.ENGLISH);
Class<T> value;
//Configuration對象執行個體化的時候(構造函數的具體邏輯),向typeAliases這個map中,放入了許多内置的值。
//比如typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
if (typeAliases.containsKey(key)) {
//如果别名集合中含有該值,從取到對應的類
value = (Class<T>) typeAliases.get(key);
} else {
//沒有,則直接調用classForName擷取到class對象
value = (Class<T>) Resources.classForName(string);
}
return value;
} catch (ClassNotFoundException e) {
throw new TypeException("Could not resolve type alias '" + string + "'. Cause: " + e, e);
}
}
public void setLogImpl(Class<? extends Log> logImpl) {
if (logImpl != null) {
//将logImpl設定到configuration的logImpl屬性中
this.logImpl = logImpl;
LogFactory.useCustomLogging(this.logImpl);
}
}
public static synchronized void useCustomLogging(Class<? extends Log> clazz) {
setImplementation(clazz);
}
private static void setImplementation(Class<? extends Log> implClass) {
try {
//擷取該日志類的構造函數
Constructor<? extends Log> candidate = implClass.getConstructor(String.class);
Log log = candidate.newInstance(LogFactory.class.getName());
if (log.isDebugEnabled()) {
log.debug("Logging initialized using '" + implClass + "' adapter.");
}
//把該日志構造函數設定到LogFactory的logConstructor屬性中
logConstructor = candidate;
} catch (Throwable t) {
throw new LogException("Error setting Log implementation. Cause: " + t, t);
}
typeAliases标簽解析
private void typeAliasesElement(XNode parent) {
if (parent != null) {
//擷取子标簽
for (XNode child : parent.getChildren()) {
//擷取package标簽
if ("package".equals(child.getName())) {
//擷取name屬性,也就是包名
String typeAliasPackage = child.getStringAttribute("name");
//看這裡
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
//配置的不是package,直接配置的别名和類的方式
String alias = child.getStringAttribute("alias");
String type = child.getStringAttribute("type");
try {
//擷取class對象
Class<?> clazz = Resources.classForName(type);
//别名沒有配置
if (alias == null) {
//找類上的Alias注解,注解也沒有,直接使用類的simpleName作為别名
typeAliasRegistry.registerAlias(clazz);
} else {
//注冊别名和類的對應關系到typeAliases中
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}
public void registerAliases(String packageName) {
//Object類型
registerAliases(packageName, Object.class);
}
public void registerAliases(String packageName, Class<?> superType) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
//找到該包下所有的class檔案
//使用classLoader.loadClass擷取對應class,如果是superType類型
//收集到resolverUtil的matches集合中
resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
//獲得resolverUtil的matches集合的類
Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses();
for (Class<?> type : typeSet) {
// Ignore inner classes and interfaces (including package-info.java)
// Skip also inner classes. See issue #6
//如果類的SimpleName不為空,不是接口 , 不是内部類
if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) {
registerAlias(type);
}
}
}
public ResolverUtil<T> find(Test test, String packageName) {
//将配置的包名中的.替換為/
String path = getPackagePath(packageName);
try {
//VFS.getInstance(),靜态内部類的方式實作的單例模式
//VFS主要用來讀取目前應用程式的資源檔案,預設有JBoss6VFS和DefaultVFS,loadCustomVfs方法會添加其他VFS的實作類
//找到所有的資源檔案
List<String> children = VFS.getInstance().list(path);
for (String child : children) {
//如果以.class結尾
if (child.endsWith(".class")) {
addIfMatching(test, child);
}
}
} catch (IOException ioe) {
log.error("Could not read package: " + packageName, ioe);
}
return this;
}
protected void addIfMatching(Test test, String fqn) {
try {
//轉換為類的完整限定名
String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.');
ClassLoader loader = getClassLoader();
if (log.isDebugEnabled()) {
log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]");
}
//使用類加載器加載該類
Class<?> type = loader.loadClass(externalName);
//使用的test為new ResolverUtil.IsA
//對應的matches為
//public boolean matches(Class<?> type) {
// return type != null && parent.isAssignableFrom(type);
//}
if (test.matches(type)) {
//添加到matches容器中
matches.add((Class<T>) type);
}
} catch (Throwable t) {
//加載不到,忽略
log.warn("Could not examine class '" + fqn + "'" + " due to a " +
t.getClass().getName() + " with message: " + t.getMessage());
}
}
public void registerAlias(Class<?> type) {
//alias為類的simpleName
String alias = type.getSimpleName();
//擷取類的Alias注解的值
Alias aliasAnnotation = type.getAnnotation(Alias.class);
//注解不為空alias為注解配置的值
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
//注冊别名和類的對應關系到typeAliases中
registerAlias(alias, type);
}
public void registerAlias(String alias, Class<?> value) {
if (alias == null) {
throw new TypeException("The parameter alias cannot be null");
}
// issue #748
//将别名傳為小寫
String key = alias.toLowerCase(Locale.ENGLISH);
if (typeAliases.containsKey(key) && typeAliases.get(key) != null && !typeAliases.get(key).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + typeAliases.get(key).getName() + "'.");
}
//放入typeAliases(map)中
typeAliases.put(key, value);
}
解析plugins标簽
private void pluginElement(XNode parent) throws Exception {
if (parent != null) {
//擷取子标簽
for (XNode child : parent.getChildren()) {
//擷取interceptor屬性值(類名或者别名都可以)
String interceptor = child.getStringAttribute("interceptor");
//擷取child 的 子标簽的name和value值
Properties properties = child.getChildrenAsProperties();
//執行個體化攔截器類
Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).newInstance();
//調用攔截器的setProperties 入參為 properties
interceptorInstance.setProperties(properties);
//将攔截器添加到configuration的interceptorChain的interceptors集合中
configuration.addInterceptor(interceptorInstance);
}
}
}
解析environments标簽
private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
//擷取default屬性指派給environment屬性
environment = context.getStringAttribute("default");
}
//循環所有的子标簽environment
for (XNode child : context.getChildren()) {
//擷取子标簽environment的id屬性
String id = child.getStringAttribute("id");
//如果id和environment相等
if (isSpecifiedEnvironment(id)) {
//解析environment标簽的transactionManager标簽
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
//解析environment标簽的dataSource标簽
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
DataSource dataSource = dsFactory.getDataSource();
//設定txFactory,dataSource和id到environmentBuilder中
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
//将environmentBuilder.build()指派給configuration的environment屬性
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}
private TransactionFactory transactionManagerElement(XNode context) throws Exception {
if (context != null) {
//擷取type屬性,事務管理器的别名或者類名
String type = context.getStringAttribute("type");
//擷取子标簽的name和value屬性封裝為Properties
Properties props = context.getChildrenAsProperties();
//執行個體化事務管理器
TransactionFactory factory = (TransactionFactory) resolveClass(type).newInstance();
//設定props的值到TransactionFactory 中
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a TransactionFactory.");
}
private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
//擷取type屬性,資料源工廠的别名或者類名
String type = context.getStringAttribute("type");
//擷取子标簽的name和value屬性封裝為Properties
Properties props = context.getChildrenAsProperties();
//執行個體化資料源工廠
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
//設定props的值到DataSourceFactory 中
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}
解析databaseIdProvider标簽
private void databaseIdProviderElement(XNode context) throws Exception {
//<databaseIdProvider type="DB_VENDOR">
// <property name="Oracle" value="oracle"/>
// <property name="MySQL" value="mysql"/>
//</databaseIdProvider>
//oracle mysql這種值用于配置到mapper.xml中的databaseId屬性中.
DatabaseIdProvider databaseIdProvider = null;
if (context != null) {
//擷取 type屬性值
String type = context.getStringAttribute("type");
// awful patch to keep backward compatibility
//如果配置的是VENDOR,則轉為DB_VENDOR,都會用VendorDatabaseIdProvider類
//typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
if ("VENDOR".equals(type)) {
type = "DB_VENDOR";
}
//擷取子标簽的name和value值封裝為properties
Properties properties = context.getChildrenAsProperties();
//執行個體化DatabaseIdProvider類型
databaseIdProvider = (DatabaseIdProvider) resolveClass(type).newInstance();
//設定properties屬性值到databaseIdProvider中
databaseIdProvider.setProperties(properties);
}
//擷取configuration的environment屬性
Environment environment = configuration.getEnvironment();
if (environment != null && databaseIdProvider != null) {
//擷取目前資料源對象對應property中的value值
String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource());
//設定value值到configuration的databaseId中
configuration.setDatabaseId(databaseId);
}
}
public String getDatabaseId(DataSource dataSource) {
if (dataSource == null) {
throw new NullPointerException("dataSource cannot be null");
}
try {
//看這裡
return getDatabaseName(dataSource);
} catch (Exception e) {
LogHolder.log.error("Could not get a databaseId from dataSource", e);
}
return null;
}
private String getDatabaseName(DataSource dataSource) throws SQLException {
//擷取databaseProductName
String productName = getDatabaseProductName(dataSource);
if (this.properties != null) {
//循環配置的所有的properties值
for (Map.Entry<Object, Object> property : properties.entrySet()) {
//如果productName包含property的key,則傳回property的value
if (productName.contains((String) property.getKey())) {
return (String) property.getValue();
}
}
// no match, return null
return null;
}
return productName;
}
private String getDatabaseProductName(DataSource dataSource) throws SQLException {
Connection con = null;
try {
//使用資料源對象擷取連接配接對象
con = dataSource.getConnection();
DatabaseMetaData metaData = con.getMetaData();
//擷取連接配接對象的DatabaseProductName , 比如 Oracle MySQL 等
return metaData.getDatabaseProductName();
} finally {
if (con != null) {
try {
con.close();
} catch (SQLException e) {
// ignored
}
}
}
}
解析typeHandlers标簽
private void typeHandlerElement(XNode parent) throws Exception {
if (parent != null) {
//擷取子标簽typeHandler或package
for (XNode child : parent.getChildren()) {
//擷取package标簽
if ("package".equals(child.getName())) {
//擷取package标簽的name屬性1
String typeHandlerPackage = child.getStringAttribute("name");
//掃描包,并且注冊typeHandle
typeHandlerRegistry.register(typeHandlerPackage);
} else {
//處理子标簽typeHandler
String javaTypeName = child.getStringAttribute("javaType");
String jdbcTypeName = child.getStringAttribute("jdbcType");
String handlerTypeName = child.getStringAttribute("handler");
//可以配置别名,使用resolveClass擷取到具體的類
Class<?> javaTypeClass = resolveClass(javaTypeName);
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
//注冊javaType和Map<JdbcType, TypeHandler<?>> map(jdbcType和TypeHandler的映射關系)的映射關系
if (javaTypeClass != null) {
if (jdbcType == null) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}
public void register(String packageName) {
ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
//掃描擷取包路徑下的所有TypeHandler類型的class
resolverUtil.find(new ResolverUtil.IsA(TypeHandler.class), packageName);
//擷取掃描到的所有的TypeHandler類
Set<Class<? extends Class<?>>> handlerSet = resolverUtil.getClasses();
for (Class<?> type : handlerSet) {
//Ignore inner classes and interfaces (including package-info.java) and abstract classes
if (!type.isAnonymousClass() && !type.isInterface() && !Modifier.isAbstract(type.getModifiers())) {
//注冊
register(type);
}
}
}
public void register(Class<?> typeHandlerClass) {
boolean mappedTypeFound = false;
//擷取MappedTypes注解
MappedTypes mappedTypes = typeHandlerClass.getAnnotation(MappedTypes.class);
if (mappedTypes != null) {
//循環MappedTypes注解的值,也就是對應的可以處理的java類型
for (Class<?> javaTypeClass : mappedTypes.value()) {
//注冊
register(javaTypeClass, typeHandlerClass);
mappedTypeFound = true;
}
}
if (!mappedTypeFound) {
//注解沒有值的一樣可以注冊
register(getInstance(null, typeHandlerClass));
}
}
private <T> void register(Type javaType, TypeHandler<? extends T> typeHandler) {
//擷取MappedJdbcTypes注解
MappedJdbcTypes mappedJdbcTypes = typeHandler.getClass().getAnnotation(MappedJdbcTypes.class);
if (mappedJdbcTypes != null) {
//循環MappedJdbcTypes注解的值,也就是對應的可以處理的jdbc類型
for (JdbcType handledJdbcType : mappedJdbcTypes.value()) {
//注冊
register(javaType, handledJdbcType, typeHandler);
}
if (mappedJdbcTypes.includeNullJdbcType()) {
//注冊
register(javaType, null, typeHandler);
}
} else {
//注冊
register(javaType, null, typeHandler);
}
}
private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
if (javaType != null) {
//使用javaType擷取到map
Map<JdbcType, TypeHandler<?>> map = typeHandlerMap.get(javaType);
if (map == null || map == NULL_TYPE_HANDLER_MAP) {
map = new HashMap<>();
//注冊javaType和Map<JdbcType, TypeHandler<?>> map的映射關系
typeHandlerMap.put(javaType, map);
}
//注冊jdbcType和typeHndler的映射關系
map.put(jdbcType, handler);
}
//建立TypeHandler的class和TypeHandler的映射關系
allTypeHandlersMap.put(handler.getClass(), handler);
}
解析mappers标簽
private void mapperElement(XNode parent) throws Exception {
if (parent != null) {
//擷取子标簽package或者mapper
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
//擷取package标簽的name屬性
String mapperPackage = child.getStringAttribute("name");
//掃描包下的xml檔案,解析加入到configuration中
//解析mapper的xml的代碼和注解裡面的指定的sql的代碼較為繁瑣這裡就不詳細展開了
//主要需要了解到:
//解析的resultMap标簽的内容會加入configuration的resultMaps(Map)中,key為namespace加resultMap标簽的id屬性
//解析的sql内容最終包裝為MappedStatement,加入到configuration的mappedStatements(map)中,key為namespace加自己标簽的id屬性
//将mapper的接口類型和生成的代理工廠類(MapperProxyFactory)注冊到Configuration的mapperRegistry的knownMappers屬性中(這個後續再詳細說)
//knownMappers.put(type, new MapperProxyFactory<>(type));
configuration.addMappers(mapperPackage);
} else {
//處理mapper标簽的屬性
//讀取到配置的xml檔案
//解析加入到configuration中
String resource = child.getStringAttribute("resource");
String url = child.getStringAttribute("url");
String mapperClass = child.getStringAttribute("class");
if (resource != null && url == null && mapperClass == null) {
ErrorContext.instance().resource(resource);
InputStream inputStream = Resources.getResourceAsStream(resource);
XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
mapperParser.parse();
} else if (resource == null && url != null && mapperClass == null) {
ErrorContext.instance().resource(url);
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.");
}
}
}
}
}
public void parseStatementNode() {
//省略部分源碼
//解析的sql内容包裝為SqlSource
//一般有動态标簽的則為DynamicSqlSource
//DynamicSqlSource含有configuration和rootSqlNode(解析出的sql内容,包裝為了MixedSqlNode,裡面持有其他的sqlNode節點,樹狀結構嵌套各個sqlNode(标簽中包含sql或者換行空格或者其他标簽))
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
//省略部分源碼
//解析的sql包裝為MappedStatement,然後加入到configuration中
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
到此解析mybatis的xml配置檔案的主要流程就結束了。