Mybatis源码-与Spring整合
目录
Mybatis与Spring整合 #
- 我们使用HSQLDB嵌入式数据库。
- 配置SqlSessionFactory对象我们知道 SqlSession 是 MyBatis 提供的与数据库交互的接口,而 SqlSession 的创建依赖于SqlSessionFactory 对象,因此我们需要创建 SqlSessionFactory 对象,并通过 Spring 来管理SqlSessionFactory对象的生命周期。
- 配置SqISessionTemplate 对象在使用MyBatis时,我们可以通过SqlSessionFactory对象的openSession()方法获取一个SqlSession对象,然后调用 SqlSession 对象提供的方法就可以与数据库进行交互了。每次调用SqlSessionFactory 对象的 openSession()方法返回的是一个新的实例,MyBatis Spring 模块提供了SqlSessionTemplate用于完成数据库交互,在整合Spring容器中只存在一个SqISessionTemplate实例。
- 通过MapperScan注解扫描Mapper接口
@SpringBootApplication
@EnableAutoConfiguration(exclude={DataSourceAutoConfiguration.class})
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
}
@Configuration
@MapperScan(basePackages = {"com.blog4java.example.mapper"},
sqlSessionTemplateRef="sqlSessionTemplate")
public class DataSourceConfiguration {
@Bean(name = "dataSource")
@Primary
public DataSource setDataSource() {
// 创建数据源Bean,并执行数据库脚本
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("create-table-c12.sql")
.addScript("init-data-c12.sql")
.build();
}
@Bean(name = "sqlSessionFactory")
public SqlSessionFactory setSqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
// 通过Mybatis Spring模块提供的SqlSessionFactoryBean
// 创建Mybatis的SqlSessionFactory对象
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:com/blog4java/example/mapper/*.xml"));
return bean.getObject();
}
@Bean(name = "sqlSessionTemplate")
@Primary
public SqlSessionTemplate setSqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
// 创建Mybatis Spring模块中的SqlSessionTemplate对象
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean(name = "transactionManager")
@Primary
public DataSourceTransactionManager setTransactionManager(@Qualifier("dataSource") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
@Bean
@Primary
public TransactionTemplate transactionTemplate(@Qualifier("transactionManager")DataSourceTransactionManager transactionManager) {
return new TransactionTemplate(transactionManager);
}
}
Spring关键接口 #
BanDefinition #
BeanDefinition用于描述SpringBean的配置信息,Spring配置Bean的方式通常有3种:
- XML配置文件,例如:
<?xml version="1.o" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/xMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.0.xsd"> <bean id="executor"class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor"> <property name="corePoolSize" value="200"/> <property name="maxPoolSize" value="500"/> <property name="queueCapacity" value="1000"/> </bean> </beans>
- Java注解,例如@Service、@Component等注解。Java注解的本质是一种轻量级的配置信息.
- JavaConfig方式,Spring从3.0版本开始支持使用@Configuration注解,通过JavaConfig方式配置Bean,这种方式在SpringBoot项目中比较流行,例如:
@Configuration public class DataSourceConfiguration { @Bean(name = "dataSource") @Primary public DataSource setDataSource() { // 创建数据源Bean,并执行数据库脚本 return new EmbeddedDatabaseBuilder() .setType(EmbeddedDatabaseType.HSQL) .addScript("create-table-c12.sql") .addScript("init-data-c12.sql") .build(); } }
BanDefinitionRegistry #
BeanDefinitionRegistry是BeanDefinition容器,所有的Bean配置解析后生成的BeanDefinition对象都会注册到BeanDefinitionRegistry对象中。Spring提供了扩展机制,允许用户在Spring框架启动时,动态地往BeanDefinitionRegistry容器中注册BeanDefinition对象。
BeanFactory #
BeanFactory是Spring的Bean工厂,负责Bean的创建及属性注入。它同时是一个Bean容器,Spring框架启动后,会根据BeanDefinition对象创建Bean实例,所有的单例Bean都会注册到BeanFactory容器中。
BeanFactoryPostProcessor #
BeanFactoryPostProcessor是Spring提供的扩展机制,用于在所有的Bean配置信息解析完成后修改Bean工厂信息。例如,向BeanDefinitionRegistry容器中增加额外的 BeanDefinition 对象,或者修改原有的BeanDefinition对象。BeanFactoryPostProcessor是一个接口,该接口中只有一个方法,具体如下:
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}
当我们配置的 Bean 实现该接口时,Spring 解析 Bean 配置完成后,就会调用所有BeanFactoryPostProcessor实现类的postProcessBeanFactory()方法。
ImportBeanDefinitionRegistrar #
ImportBeanDefinitionRegistrar是一个接口,该接口的实现类作用于Spring解析Bean的配置阶段,当解析@Configuration 注解时,可以通过ImportBeanDefinitionRegistrar接口的实现类向BeanDefinitionRegistry容器中添加额外的 BeanDefinition对象。ImportBeanDefinitionRegistrar接口定义如下:
public interface ImportBeanDefinitionRegistrar {
void registerBeanDefinitions(AnnotationMetadata var1, BeanDefinitionRegistry var2);
}
ImportBeanDefinitionRegistrar 接口实现类的registerBeanDefinitions()方法会在 Spring 解析@Configuration 注解时调用。ImportBeanDefinitionRegistrar 接口需要配合@Import 注解使用,importingClassMetadata参数为@Import所在注解的配置信息,registry参数为BeanDefinition容器。
BeanPostProcessor #
Bean的后置处理器,在Bean初始化方法(init-method属性指定的方法或afterPropertiesSet()方法)调用前后,会执行BeanPostProcessor中定义的拦截逻辑。BeanPostProcessor接口定义如下:
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
BeanPostProcessor 接口中定义了两个方法,postProcessBeforeInitialization()方法会在所有Bean初始化方法调用之前执行,postProcessAfterInitialization()方法会在所有Bean的初始化方法调用之后执行。BeanPostProcessor 通常用于处理 SpringBean对应的Java类中的注解信息或者创建 Bean的代理对象。
ClassPathBeanDefinitionScanner #
ClassPathBeanDefinitionScanner是BeanDefinition扫描器,能够对指定包下的Class进行扫描,将Class信息转换为BeanDefinition对象注册到BeanDefinitionRegistry容器中。ClassPathBeanDefinitionScanner支持自定义的过滤规则,例如我们可以只对使用某种注解的类进行扫描。Spring中的@Service、@Component等注解配置Bean都是通过ClassPathBeanDefinitionScanner 实现的。MyBatis Spring 模块中 Mapper 接口的扫描使用到了ClassPathBeanDefinitionScanner类。
FactoryBean #
FactoryBean是 Spring中的工厂Bean,通常用于处理Spring中配置较为复杂或者由动态代理生成的Bean实例。实现了该接口的Bean不能作为普通的Bean使用,而是作为单个对象的工厂。当我们通过Bean 名称获取FactoryBean 实例时,获取到的并不是FactoryBean 对象本身,而是FactoryBean对象的getObject()方法返回的实例。例如如下Bean配置:
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
</bean>
SqlSessionFactoryBean是一个FactoryBean,通过名称 sqlSessionFactory从 Spring容器中获取Bean时,获取到的实际上是SqlSessionFactoryBean对象的getObject()方法返回的对象。
Spring启动过程 #
Mapper动态代理对象注册过程 #
从MapperScan 注解中开始追踪
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
// 扫描包路径
String[] value() default {};
// 扫描包路径
String[] basePackages() default {};
// 扫描Mapper接口对应的class对象
Class<?>[] basePackageClasses() default {};
// ean名称生成策略
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
// 扫描使用某种注解修饰的类或接口
Class<? extends Annotation> annotationClass() default Annotation.class;
// 扫描某种类型的子类型
Class<?> markerInterface() default Class.class;
// 指定使用哪个SqlSessionTemplate对象
String sqlSessionTemplateRef() default "";
// 指定使用哪个SqlSessionFactory对象
String sqlSessionFactoryRef() default "";
// 指定使用自定义的MapperFactoryBean 返回Mybatis代理对象作为 Spring的 Bean
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
}
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
/**
* {@inheritDoc}
*/
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
/**
* {@inheritDoc}
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
// 调用registerBeanDefinitions()方法注册BeanDefinition对象
registerBeanDefinitions(mapperScanAttrs, registry);
}
}
void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry) {
// ClassPathMapperScanner是Mybatis Spring模块自定义的BeanDefinition扫描器
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
if (resourceLoader != null) {
scanner.setResourceLoader(resourceLoader);
}
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
scanner.setAnnotationClass(annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
scanner.setMarkerInterface(markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
}
// 获取注解配置信息
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
List<String> basePackages = new ArrayList<>();
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("value"))
.filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("basePackages"))
.filter(StringUtils::hasText)
.collect(Collectors.toList()));
// 添加需要扫描的包
basePackages.addAll(
Arrays.stream(annoAttrs.getClassArray("basePackageClasses"))
.map(ClassUtils::getPackageName)
.collect(Collectors.toList()));
// 注册扫描过滤规则
scanner.registerFilters();
// 对包中的类进行扫描生成BeanDefinition对象
scanner.doScan(StringUtils.toStringArray(basePackages));
}
/**
* A {@link MapperScannerRegistrar} for {@link MapperScans}.
* @since 2.0.0
*/
static class RepeatingRegistrar extends MapperScannerRegistrar {
/**
* {@inheritDoc}
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScansAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScans.class.getName()));
if (mapperScansAttrs != null) {
Arrays.stream(mapperScansAttrs.getAnnotationArray("value"))
.forEach(mapperScanAttrs -> registerBeanDefinitions(mapperScanAttrs, registry));
}
}
}
}
MapperScannerRegistrar 类实现了ImportBeanDefinitionRegistrar 接口的registerBeanDefinitions()方法,该方法调用了重载的registerBeanDefinitions()进行处理。在重载方法中,首先创建了一个ClassPathMapperScanner 对象,然后获取MapperScan注解的属性信息,根据MapperScan的annotationClass 和markerInterface 属性对扫描的Class 进行过滤,最后调用ClassPathMapperScanner对象的doScan()方法进行扫描。ClassPathMapperScanner 是Spring中ClassPathBeanDefinitionScanner的子类,用于扫描特定包下的Mapper 接口,将Mapper 接口信息转换为对应的BeanDefinition对象。下面是ClassPathMapperScanner 类doScan()方法的实现:
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 调用父类的doScan()方法,將包中的Class转换为BeanDefinitionHolder对象
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
if (beanDefinitions.isEmpty()) {
LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
} else {
// 对BeanDefinitionHolder进行处理
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
// 获取BeanDefinition对象
definition = (GenericBeanDefinition) holder.getBeanDefinition();
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName()
+ "' and '" + beanClassName + "' mapperInterface");
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
// 將BeanDefinition对象的beanClass属性设置为MapperFactoryBean
definition.setBeanClass(this.mapperFactoryBean.getClass());
// 修改BeanDefinition对象的propertyValues属性,將sqlSessionFactory注入到MapperFactoryBean中
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) {
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) {
LOGGER.warn(() -> "Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
}
}
如上面的代码所示,在ClassPathMapperScanner 类的doScan()方法中,首先调用父类的doScan()方法,将指定包下的Mapper接口信息转换为BeanDefinitionHolder对象,BeanDefinitionHolder 中持有一个BeanDefinition对象及Bean的名称和所有别名。所有的Mapper接口转换为BeanDefinitionHolder 对象后,接着调用processBeanDefinitions()方法,对所有BeanDefinitionHolder对象进行处理。在processBeanDefinitions()方法中,对所有BeanDefinitionHolder 对象进行遍历,获取BeanDefinitionHolder对象中持有的BeanDefinition对象。然后对BeanDefinition对象的信息进行修改,将BeanDefinition对象的beanClass属性设置为MapperFactoryBean,并向BeanDefinition对象中增加几个PropertyValue对象,对应MapperFactoryBean的addToConfig和 sqlSessionTemplate等属性。
将BeanDefinition 对象的beanClass属性设置为MapperFactoryBean这一步很重要,当 Spring将所有的Bean配置信息转换为BeanDefinition对象后,就会根据BeanDefinition对象来实例化Bean。由于BeanDefinition对象的beanClass属性被设置为MapperFactoryBean,因此 Spring在创建Bean时实例化的是MapperFactoryBean对象。Spring会根据BeanDefinition对象中的PropertyValues对象对MapperFactoryBean 对象进行属性填充,因此MapperFactoryBean 对象的addToConfig和sqlSessionTemplate属性会被自动注入。
MapperFactoryBean 实现了FactoryBean 接口。当我们根据Mapper类型从Spring容器中获取FactoryBean时,获取到的并不是FactoryBean本身,而是FactoryBean的getObject()方法返回的对象。我们可以关注一下MapperFactoryBean的getObject()方法的实现,代码如下:
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
}
这里我们找到了想要的答案,在MapperFactoryBean的getObject()方法中,调用SqISession对象的getMapper()方法返回一个Mapper动态代理对象。
整合Spring事务管理 #
暂时搁置。