我们平时在项目中都是用Spring来管理的,那么,Spring是如何管理MyBatis的呢?我们来一探究竟。
要了解Spring是如何加载MyBatis的,我想还是先来回顾一下我们是如何用编程的方式去加载MyBatis框架的
String resource = "mybatis/conf/mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
//从 XML 中构建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
//获取session
SqlSession session = sqlSessionFactory.openSession();
try {
//获取Mapper
BlogMapper mapper = session.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(1L);
System.out.println(blog);
// blog = mapper.selectBlog(1L);
// System.out.println(blog);
} finally {
session.close();
}
在代码中,我们主要是构建SqlSessionFactory,而构建 SqlSessionFactory,需要配置文件。那么Spring中我们是怎么使用的呢?
dialect=mysql
从配置中,我们可以看出,这里做了两步操作:
首先我们看下SqlSessionFactory类的结构
SqlSessionFactory实现了InitializingBean接口,也就是说,在该Bean初始化的时候,我执行InitializingBean中的afterPropertiesSet()方法。那么,我们直接进入SqlSessionFactory的afterPropertiesSet()方法:
public void afterPropertiesSet() throws Exception {
//...
//构建SqlSessionFactory
this.sqlSessionFactory = buildSqlSessionFactory();
}
在该方法中,直接进行构建SqlSessionFactory,那么来看下构建SqlSessionFactory的过程方法(删去了部分):
protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
Configuration configuration;
XMLConfigBuilder xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
configuration = xmlConfigBuilder.getConfiguration();
configuration.setObjectFactory(this.objectFactory);
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
}
for (Class> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
}
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
}
String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
configuration.getTypeHandlerRegistry().register(packageToScan);
}
for (TypeHandler> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
}
xmlConfigBuilder.parse();
configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
for (Resource mapperLocation : this.mapperLocations) {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
}
}
return this.sqlSessionFactoryBuilder.build(configuration);
}
//在上面的方法中主要进行的操作是:整理加载配置信息Configuration,使用这些配置通过SqlSessionFactoryBuilder创建SqlSessionFactory,在最后调用了sqlSessionFactoryBuilder.build,我们看下这个是什么:
深入代码,你就能看到这是重载的build方法,之前编程式加载MyBatis的时候调用的是上面的方法。
SqlSessionFactory已经创建完成了,那么,接下来就应该把Mapper装入Spring容器了。
我们是如何从Spring中拿Mapper对象的,这主要是MapperFactoryBean类的作用:
这是Mapper的获取过程:
/**
* {@inheritDoc}
*/
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
接下来就是Mappers注入到Spring容器管理的过程了:
它实现了BeanDefinitionRegistryPostProcessor接口,MapperScannerConfigurer->postProcessBeanDefinitionRegistry()
BeanDefinitionRegistryPostProcessor 是 BeanFactoryPostProcessor 的子类,可以通过编码的方式,改变、新增类的定义,甚至删除某些 bean 的定义
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.registerFilters();
//把定义的mapper包下的所有bean注册到Spring容器中
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
Spring加载MyBatis这个过程,其实就是把MyBatis的Mapper转换成Bean,注入到Spring容器的过程。学习这些内容不只是帮助我们了解MyBatis是怎么样结合Spring的,也让我们了解或许大多数框架结合Spring就是这样的流程。