概要
过度
我们上面介绍了 MyBatis 的使用方法和 Spring MyBatis 的使用方法。本文着重介绍 Spring MyBatis 的封装。当然,这里只是着重介绍针对 MyBatis API 的封装,不介绍包的扫描逻辑。
我们要介绍的主要有两个地方,在xml中的位置分别如下:
其实就是两个类。
内容简介
本文主要介绍SqlSessionFactoryBean
、MapperFactoryBean
两个类的内部逻辑。
所属环节
对 spring-mybatis 的实现详情的介绍。
上下环节
上文: spring-mybatis 的引入
下文: 对 spring-mybatis 中涉及的高级功能——包扫描及BD注册的探索
SqlSessionFactoryBean
源码
入口
在Spring中注册了这个BD,没有额外做其他操作,我们看一下SqlSessionFactoryBean
的继承关系:
我们发现它实现了三个比较有意思的接口:
-
InitializingBean
:此函数有初始化钩子,应该是用于配置加载和整合 -
FactoryBean
:他是FactoryBean
,猜测生成目标类型的实例可能存在一些逻辑 -
ApplicationListener
:他监听着ApplicationContext
的事件,猜测可能根据事件做出一些变化,如更新配置、控制一些生命周期之类的吧
InitializingBean
public void afterPropertiesSet() throws Exception {
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
"Property 'configuration' and 'configLocation' can not specified with together");
this.sqlSessionFactory = buildSqlSessionFactory();
}
这里仅仅做了一些非空判断,然后将逻辑继续委托,和Spring的代码逻辑很像。注意记住这里是configuration
和configLocation
不能同时配置。
我们继续看核心逻辑的方法:
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;
XMLConfigBuilder xmlConfigBuilder = null;
// 如果配置的 Configuration 不为空,就把我们在 Spring 的xml中配置的属性填进去
if (this.configuration != null) {
configuration = this.configuration;
if (configuration.getVariables() == null) {
configuration.setVariables(this.configurationProperties);
} else if (this.configurationProperties != null) {
configuration.getVariables().putAll(this.configurationProperties);
}
} else if (this.configLocation != null) {
// 否则如果你配置了 MyBatis 的配置文件地址,就从那里找值、加载
xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
} else {
// 如果都没有配置,那就自己new一个配置对象,然后把你在 Spring 的xml中配置的属性填进去
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
}
configuration = new Configuration();
if (this.configurationProperties != null) {
configuration.setVariables(this.configurationProperties);
}
}
// 到此为止我们设置了一些基本属性,如果配置了MyBatis的配置文件,就从文件中加载了所有的配置
// 但是Spring允许从它的xml中重复配置,而且这里的配置优先级会更高
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
if (this.vfs != null) {
configuration.setVfsImpl(this.vfs);
}
if (hasLength(this.typeAliasesPackage)) {
String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
}
if (!isEmpty(this.typeAliases)) {
for (Class> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type alias: '" + typeAlias + "'");
}
}
}
if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered plugin: '" + plugin + "'");
}
}
}
if (hasLength(this.typeHandlersPackage)) {
String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
configuration.getTypeHandlerRegistry().register(packageToScan);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
}
if (!isEmpty(this.typeHandlers)) {
for (TypeHandler> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Registered type handler: '" + typeHandler + "'");
}
}
}
if (this.databaseIdProvider != null) {//fix #64 set databaseId before parse mapper xmls
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
if (this.cache != null) {
configuration.addCache(this.cache);
}
if (xmlConfigBuilder != null) {
try {
xmlConfigBuilder.parse();
if (LOGGER.isDebugEnabled()) {
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();
}
}
// 设置事务管理
if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
// 这里配置了mapper的xml的具体位置
if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
if (LOGGER.isDebugEnabled()) {
LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
}
// 配置完成,这里构建 SqlSessionFactory 并返回
return this.sqlSessionFactoryBuilder.build(configuration);
}
FactoryBean
public SqlSessionFactory getObject() throws Exception {
if (this.sqlSessionFactory == null) {
afterPropertiesSet();
}
return this.sqlSessionFactory;
}
基本没有逻辑。
ApplicationListener
public void onApplicationEvent(ApplicationEvent event) {
if (failFast && event instanceof ContextRefreshedEvent) {
// fail-fast -> check all statements are completed
this.sqlSessionFactory.getConfiguration().getMappedStatementNames();
}
}
如果是ApplicationContext
的刷新操作,就刷一遍Statement
。(这里我也不太懂是做些啥)
总结
这里完成了SqlSessionFactory
的生成。
MapperFactoryBean
源码
入口
我们指定的类型是MapperFactoryBean
,看样子是个FactoryBean
,要根据内部逻辑来判断它返回BD 的类型。
我们看一下这个类的继承关系:
个人感觉InitializingBean
可能涉及一些初始化的问题
FactoryBean
中涉及一些对BD的处理,还有对Mapper实例的构建
InitializingBean
@Override
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
// Let abstract subclasses check their configuration.
checkDaoConfig();
// Let concrete implementations initialize themselves.
try {
initDao();
}
catch (Exception ex) {
throw new BeanInitializationException("Initialization of DAO failed", ex);
}
}
@Override
protected void checkDaoConfig() {
notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
}
protected void checkDaoConfig() {
super.checkDaoConfig();
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
configuration.addMapper(this.mapperInterface);
} catch (Exception e) {
logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
throw new IllegalArgumentException(e);
} finally {
ErrorContext.instance().reset();
}
}
}
protected void initDao() throws Exception {
}
我们发现初始化没搞什么特别的东西吧,校验什么的也不是是因为在configuration
中维护了Class
和Mapper
实例的映射。具体为什么要做这个,如何维护的映射关系,这里涉及MyBatis内部逻辑了,先不盲目深入。
想想也对,所有的在SqlSessionFactoryBean
都做好了,这里就等生成Mapper实例了。
FactoryBean
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
@Override
public Class getObjectType() {
return this.mapperInterface;
}
总结
到这里,完成了对 Mapper 实例的生成。
总结
整体来说思路比较清晰。没什么说的。