Myabtis源码——整合IOC操作
SqlSessionFactoryBean就指定好了mapperLocations、dataSource、configLocations
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd">
<bean class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="mapperLocations" value="classpath:com/easyworld/object/Sql/*.xml" />
<property name="configLocations" value="mybatis-config.xml"></property>
</bean>
</beans>
省略,不作为本章重点
<?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>
……
</configuration>
Myabtis源码——整合IOC原理
解析配置——parese
IOC如何通过SqlSessionFactoryBean来将myabtis加入的ioc容器中;
public class SqlSessionFactoryBean
implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {
/*
getObject是FactoryBean对IOC容器暴露的创建bean的方法
第一次创建则调用afterPropertiesSet,再返回sqlSessionFactory
*/
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
/*
创建sqlSessionFactory方法调用连接
*/
public void afterPropertiesSet() throws Exception {
this.sqlSessionFactory = buildSqlSessionFactory();
}
/*
创建sqlSessionFactory方法执行细节
*/
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
final Configuration targetConfiguration;
XMLConfigBuilder xmlConfigBuilder = null;
/*
(1)SqlSessionFactoryBean初始化属性应用
根据SqlSessionFactoryBean的配资来完成一些初始化,并创建XMLConfigBuilder解析config核心配资文件
*/
if (this.configuration != null) {
targetConfiguration = this.configuration;
if (targetConfiguration.getVariables() == null) {
targetConfiguration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
targetConfiguration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
targetConfiguration = xmlConfigBuilder.getConfiguration();
} else {
LOGGER.debug(
() -> "Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
targetConfiguration = new Configuration();
Optional.ofNullable(this.configurationProperties).ifPresent(targetConfiguration::setVariables);
}
Optional.ofNullable(this.objectFactory).ifPresent(targetConfiguration::setObjectFactory);
Optional.ofNullable(this.objectWrapperFactory).ifPresent(targetConfiguration::setObjectWrapperFactory);
Optional.ofNullable(this.vfs).ifPresent(targetConfiguration::setVfsImpl);
/*
scanClasses——registerAlias
注解别名
*/
if (hasLength(this.typeAliasesPackage)) {
scanClasses(this.typeAliasesPackage, this.typeAliasesSuperType).stream()
.filter(clazz -> !clazz.isAnonymousClass()).filter(clazz -> !clazz.isInterface())
.filter(clazz -> !clazz.isMemberClass()).forEach(targetConfiguration.getTypeAliasRegistry()::registerAlias);
}
/*
利用registerAlias
*/
if (!isEmpty(this.typeAliases)) {
Stream.of(this.typeAliases).forEach(typeAlias -> {
targetConfiguration.getTypeAliasRegistry().registerAlias(typeAlias);
LOGGER.debug(() -> "Registered type alias: '" + typeAlias + "'");
});
}
/*
利用插件添加sql拦截器
*/
if (!isEmpty(this.plugins)) {
Stream.of(this.plugins).forEach(plugin -> {
targetConfiguration.addInterceptor(plugin);
LOGGER.debug(() -> "Registered plugin: '" + plugin + "'");
});
}
/*
注册类型处理器 getTypeHandlerRegistry.register
*/
if (hasLength(this.typeHandlersPackage)) {
scanClasses(this.typeHandlersPackage, TypeHandler.class).stream().filter(clazz -> !clazz.isAnonymousClass())
.filter(clazz -> !clazz.isInterface()).filter(clazz -> !Modifier.isAbstract(clazz.getModifiers()))
.forEach(targetConfiguration.getTypeHandlerRegistry()::register);
}
if (!isEmpty(this.typeHandlers)) {
Stream.of(this.typeHandlers).forEach(typeHandler -> {
targetConfiguration.getTypeHandlerRegistry().register(typeHandler);
LOGGER.debug(() -> "Registered type handler: '" + typeHandler + "'");
});
}
……
/*
(2)核心配置文件解析
xmlConfigBuilder.parse() 核心
*/
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
LOGGER.debug(() -> "Parsed configuration file: '" + this.configLocation + "'");
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
targetConfiguration.setEnvironment(new Environment(this.environment,
this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
this.dataSource));
/*
(3)mapper文件解析
mapperLocations
*/
if (this.mapperLocations != null) {
if (this.mapperLocations.length == 0) {
LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
} else {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
LOGGER.debug(() -> "Property 'mapperLocations' was not specified.");
}
return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}
/*
*/
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
}
XmlConfigBuilder
XMLMapperBuilder
蕴含了复杂的解析规则,不再继续深入
public class XMLMapperBuilder extends BaseBuilder {
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
}
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();
}
}
Configuration 作为配置的内存保存的地方 而BaseBuilder内置了大量的typeAliasRegistry
typeHandlerRegistry 来完成类型和别名映射
保存配置——Configuration
了解一下数据结构就行
public class Configuration {
protected Environment environment;//环境变量对象
// 允许在嵌套语句中使用分页(RowBounds),如果允许使用则设置成false。默认为false.
protected boolean safeRowBoundsEnabled;
// 允许在嵌套语句中使用分页。如果允许设置为false.
protected boolean safeResultHandlerEnabled = true;
// 是否开启自动驼峰命名规则(camel case)映射。如user_name ---> userName,默认false
protected boolean mapUnderscoreToCamelCase;
// 当开启时,任何方法的调用都会加载该对象的所有属性。否则,每个属性则按需加载。默认是false.
protected boolean aggressiveLazyLoading;
// 是否允许单一语句返回多结果集(需要兼容驱动)
protected boolean multipleResultSetsEnabled = true;
// 允许JDBC自动生成主键,需要驱动兼容。这就是insert时获取mysql自增或oralce sequence的开关。开发中,一般希望插入后获取主键,所以设置成true.
protected boolean useGeneratedKeys;
// 使用列标签代替列名,开发中一半进行配置。
protected boolean useColumnLabel = true;
// 是否开启缓存
protected boolean cacheEnabled = true;
// 指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这对于有 Map.keySet() 依赖或 null 值初始化的时候是有用的
protected boolean callSettersOnNulls;
// 允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的工程必须采用Java 8编译,并且加上-parameters选项。(从3.4.1开始)
protected boolean useActualParamName = true;
// 当返回行的所有列都是空时,MyBatis默认返回null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集 (i.e. collectioin and association)。(从3.4.2开始) 注:这里应该拆分为两个参数比较合适, 一个用于结果集,一个用于单记录。通常来说,我们会希望结果集不是null,单记录仍然是null
protected boolean returnInstanceForEmptyRow;
// 指定 MyBatis 增加到日志名称的前缀。
protected String logPrefix;
// 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。一般建议指定为slf4j或log4j
protected Class <? extends Log> logImpl;
// 指定VFS的实现, VFS是mybatis提供的用于访问AS内资源的一个简便接口
protected Class <? extends VFS> vfsImpl;
// MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据
protected LocalCacheScope localCacheScope = LocalCacheScope.SESSION;
// 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER
protected JdbcType jdbcTypeForNull = JdbcType.OTHER;
// 指定对象的哪个方法触发一次延迟加载
protected Set<String> lazyLoadTriggerMethods = new HashSet<String>(Arrays.asList(new String[] { "equals", "clone", "hashCode", "toString" }));
// 设置超时时间,它决定驱动等待数据库响应的秒数。默认不超时
protected Integer defaultStatementTimeout;
// 为驱动的结果集设置默认获取数量
protected Integer defaultFetchSize;
// SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新
protected ExecutorType defaultExecutorType = ExecutorType.SIMPLE;
// 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)
protected AutoMappingBehavior autoMappingBehavior = AutoMappingBehavior.PARTIAL;
// 指定发现自动映射目标未知列(或者未知属性类型)的行为。这个值应该设置为WARNING比较合适
protected AutoMappingUnknownColumnBehavior autoMappingUnknownColumnBehavior = AutoMappingUnknownColumnBehavior.NONE;
// settings下的properties属性
protected Properties variables = new Properties();
// 默认的反射器工厂,用于操作属性、构造器方便
protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory();
// 对象工厂, 所有的类resultMap类都需要依赖于对象工厂来实例化
protected ObjectFactory objectFactory = new DefaultObjectFactory();
// 对象包装器工厂,主要用来在创建非原生对象,比如增加了某些监控或者特殊属性的代理类
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
// 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态
protected boolean lazyLoadingEnabled = false;
// 指定 Mybatis 创建具有延迟加载能力的对象所用到的代理工具。MyBatis 3.3+使用JAVASSIST
protected ProxyFactory proxyFactory = new JavassistProxyFactory(); // #224 Using internal Javassist instead of OGNL
// MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。
protected String databaseId;
/**
* Configuration factory class.
* Used to create Configuration for loading deserialized unread properties.
*
* @see <a href='https://code.google.com/p/mybatis/issues/detail?id=300'>Issue 300 (google code)</a>
*/
protected Class<?> configurationFactory;
protected final MapperRegistry mapperRegistry = new MapperRegistry(this);
// mybatis插件列表
protected final InterceptorChain interceptorChain = new InterceptorChain();
// 类型注册器, 用于在执行sql语句的出入参映射以及mybatis-config文件里的各种配置比如<transactionManager type="JDBC"/><dataSource type="POOLED">时使用简写, 后面会详细解释
protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();
protected final TypeAliasRegistry typeAliasRegistry = new TypeAliasRegistry();
protected final LanguageDriverRegistry languageRegistry = new LanguageDriverRegistry();
/*
以下三个见名思其意
mappedStatements就是用key,map保存mapper
*/
protected final Map<String, MappedStatement> mappedStatements = new StrictMap<MappedStatement>("Mapped Statements collection");
protected final Map<String, Cache> caches = new StrictMap<Cache>("Caches collection");
protected final Map<String, ResultMap> resultMaps = new StrictMap<ResultMap>("Result Maps collection");
protected final Map<String, ParameterMap> parameterMaps = new StrictMap<ParameterMap>("Parameter Maps collection");
protected final Map<String, KeyGenerator> keyGenerators = new StrictMap<KeyGenerator>("Key Generators collection");
protected final Set<String> loadedResources = new HashSet<String>();
protected final Map<String, XNode> sqlFragments = new StrictMap<XNode>("XML fragments parsed from previous mappers");
protected final Collection<XMLStatementBuilder> incompleteStatements = new LinkedList<XMLStatementBuilder>();
protected final Collection<CacheRefResolver> incompleteCacheRefs = new LinkedList<CacheRefResolver>();
protected final Collection<ResultMapResolver> incompleteResultMaps = new LinkedList<ResultMapResolver>();
protected final Collection<MethodResolver> incompleteMethods = new LinkedList<MethodResolver>();
}