说一下写这篇文章的目的,是为了帮助大家梳理一下在spring环境下我们需要做哪些工作去集成mybatis,以及为什么这么去做。好了下面进入我们的本次议题。
首先说一下mybatis的几个关键核心类,及他们的之间的关系。第一个SqlSessionFactory,用来创建SqlSession接口的实例的,默认是DefaultSqlSessionFactory类型的对象。第二个是SqlSession,通过他我们可以管理Connection和获取mapper 映射器实例的动态代理对象,从而进行dao 查询.到此基础的核心类就介绍到这。
接着说一下和spring集成时的一些操作
或者开启包扫描
@Configuration
@MapperScan("org.mybatis.spring.sample.mapper")
public class AppConfig {
// ...
}
好,最后我们总结一下spring集成mybatis所做的两个操作:1 创建sqlSessionFactoryBean 2 通过MapperBeanFactory或自动扫描创建mapper.那么@MapperScan 这个注解为我们做了什么操作,就能够将mapper代理对象放到ioc容器中去呢?
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MapperScannerRegistrar.class})//引入这个类
public @interface MapperScan {
String[] value() default {};
String[] basePackages() default {};
Class>[] basePackageClasses() default {};
Class extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class extends Annotation> annotationClass() default Annotation.class;
Class> markerInterface() default Class.class;
String sqlSessionTemplateRef() default "";
String sqlSessionFactoryRef() default "";
Class extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
}
//mybatis version : 1.3.1.jar
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
public MapperScannerRegistrar() {
}
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
//此处进行包的扫描
scanner.doScan(StringUtils.toStringArray(basePackages));
}
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
public Set doScan(String... basePackages) {
Set beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
this.logger.warn("No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
//这个这个函数的关键位置
this.processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
private void processBeanDefinitions(Set beanDefinitions) {
Iterator var3 = beanDefinitions.iterator();
while(var3.hasNext()) {
BeanDefinitionHolder holder = (BeanDefinitionHolder)var3.next();
GenericBeanDefinition definition = (GenericBeanDefinition)holder.getBeanDefinition();
if (this.logger.isDebugEnabled()) {
this.logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + definition.getBeanClassName() + "' mapperInterface");
}
//将我们原有的bean信息中class 作为构造函数参数传入构造函数 definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
//将我们扫描到的mapper的bean的信息中class 的信息替换为 mapperFactoryBean.class
definition.setBeanClass(this.mapperFactoryBean.getClass());
definition.getPropertyValues().add("addToConfig", this.addToConfig);
boolean explicitFactoryUsed = false;
if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionFactory != null) {
definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
explicitFactoryUsed = true;
}
if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
if (explicitFactoryUsed) {
this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
this.logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
}
//并将这个bean设置为属性自动注入,这摸做的目的是将sessionfactory 注入进去
definition.setAutowireMode(2);
}
}
现在我们容器中的mapper接口现在已经替换为了MapperFactoryBean了。既然他是一个FactoryBean,我们必然会调用.getObject()方法来获取mapper,那么我们在来看看MapperFactoryBean中做了什么?
public T getObject() throws Exception {
//这通过 sqlSession 进行mapper 动态代理的创建,(1)但是sqlSession 是谁创建的呢?(2)默认的sqlSession并不是线程安全的,每个mapper一个mapperFactorybean会不会造成线程不安全问题呢?
return this.getSqlSession().getMapper(this.mapperInterface);
}
此时我们在关联上上面的设置自动注入属性就会清晰很多了。
//sqlSessionFactory属性注入时
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
if (!this.externalSqlSession) {
//创建一个SqlSessionTemplate ,我这边需要说明一下,他实现了sqlSession接口,并且它是线程安全的
this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
}
}
到此为止,spring集成mybatis的所有代码流程梳理就介绍完了。
我们来在总结一下:
1) 添加注解@MapperScan时引入MapperScannerRegistrar
2)在MapperScannerRegistrar 中调用doScan获取扫描包的信息
3) doScan调用processBeanDefinitions()方法
4)在这里面将扫描的Mapper接口的BeanDefinition的信息进行篡改 ,class设置为mapperFactoryBean ,构造函数参数设置为mapper接口名称,设置自动属性注入
5) 自动属性注入时,会注入sqlSessionFctory时创建一个线程安全的sqlSession
6)当我们getBean获取mapper时,会通过getObject方法通过sqlSession.getMapper()方法获取我们所需要的动态代理对象
ok,这个MyBatis-spring集成的东西就这样吧。下面我们在来看看springBoot中是如何做的。
//mybatis-spring-boot-autoconfigure version 1.3.0.jar
@Configuration
//mybatis的属性对象封装类,并放入我们bean中
@EnableConfigurationProperties({MybatisProperties.class})
public class MybatisAutoConfiguration {
...
...
...
//创建SqlSessionFactory
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
...
return factory.getObject();
}
//创建SqlSessionTemplate
@Bean
@ConditionalOnMissingBean
public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
ExecutorType executorType = this.properties.getExecutorType();
return executorType != null ? new SqlSessionTemplate(sqlSessionFactory, executorType) : new SqlSessionTemplate(sqlSessionFactory);
}
@Configuration
//引入AutoConfiguredMapperScannerRegistrar
@Import({MybatisAutoConfiguration.AutoConfiguredMapperScannerRegistrar.class})
@ConditionalOnMissingBean({MapperFactoryBean.class})
public static class MapperScannerRegistrarNotFoundConfiguration {
...这里面没有什么卵用
}
public static class AutoConfiguredMapperScannerRegistrar implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
public AutoConfiguredMapperScannerRegistrar() {
}
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper");
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
//这又是之前我们的逻辑了
scanner.doScan(StringUtils.toStringArray(packages));
}
}
}