目录
一、Mybatis和Spring集成项目准备
学习本篇前可先看(六)Mybatis持久化框架原理之MapperScannerConfigurer方式和Spring集成,了解最基本的mybatis-spring集成方式后再来学习此方式效果更好。
第五篇中的applicationContext.xml文件、datasource.properties文件以及mybatis-config.xml这三个文件在此demo中可以删除,新增以下两个类:
1.DbConfig配置类
源码如下:
@Configuration
@MapperScans(
@MapperScan(basePackages = SystemCst.BASE_PACKAGE,
sqlSessionTemplateRef = SystemCst.SQL_SESSION_TEMPLATE)
)
public class DbConfig {
@Bean(SystemCst.DATASOURCE)
public DataSource getDataSource() {
OracleDataSource dataSource = null;
try {
dataSource = new OracleDataSource();
dataSource.setDriverType(SystemCst.DRIVER);
dataSource.setURL(SystemCst.URL);
dataSource.setUser(SystemCst.USERNAME);
dataSource.setPassword(SystemCst.PASSWORD);
} catch (SQLException e) {
e.printStackTrace();
}
return dataSource;
}
@Bean(SystemCst.SQL_SESSION_FACTORY)
public SqlSessionFactory getSqlSessionFactoryBean(
@Qualifier(SystemCst.DATASOURCE) DataSource dataSource) {
SqlSessionFactoryBean sqlSessionFactoryBean =
new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource(dataSource);
PathMatchingResourcePatternResolver resolver =
new PathMatchingResourcePatternResolver();
try {
sqlSessionFactoryBean.setMapperLocations(
resolver.getResources(SystemCst.MAPPER_LOCATION));
} catch (IOException e) {
e.printStackTrace();
}
try {
return sqlSessionFactoryBean.getObject();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
2.SystemCst常量类
源码如下,配置都是可替换的:
public interface SystemCst {
String DATASOURCE = "datasource";
String SQL_SESSION_FACTORY= "sqlSessionFactory";
String SQL_SESSION_TEMPLATE = "sqlSessionTemplate";
String DRIVER = "****";
String URL = "****";
String USERNAME = "****";
String PASSWORD = "****";
String MAPPER_LOCATION = "classpath*:*Mapper.xml";
String BASE_PACKAGE = "com.**.mapper";
}
按上述配置完就可以直接运行了,当然还有其他的配置方式,但只要搞懂这些方式的原理,就可以理解任何一种配置方式。
二、UML类图和流程分析
1.UML类图分析
该配置方式涉及的类图:
该图相比于第六篇而言,大致没什么变化,并且把上一篇已经介绍过的接口剔除了此种配置相比而言多了MapperScannerRegistrar以及它实现的接口,详细介绍如下:
- ResourceLoaderAware接口:该接口将会在ResourceLoader(ApplicationCOntext一般而言会实现该接口)运行时调用接口方法setResourceLoader,也可以使用spring的接口ApplicationContextAware来代替,目前mybatis-spring对于该接口的实现为空;
- ImportBeanDefinitionRegistrar接口:处理@Configuration注解类时注册额外的bean定义;
- MapperScannerRegistrar类:对mybatis注解@MapperScan和@MapperScans进行处理的类,通过读取注解中的值在方法中修改MapperScannerConfigurer bean定义,从而实现用MapperScannerConfigurer类来管理mybatis和spring的集成。
2.流程分析
其流程图如下:
可以看到其相较于上一篇介绍的集成方式而言,中间只是多了@MapperScan注解引入MapperScannerRegistrar类,再通过这个类注册MapperScannerConfigurer类及配置注解中相应的属性即可完成集成。当Spring工厂有MapperScannerConfigurer这个类的bean定义后,在处理BeanFactoryPostProcessor接口时将会获取MapperScannerConfigurer类,进而完成第六章所述流程。
三、源码分析
1.MapperScannerConfigurer类
该类实现ImportBeanDefinitionRegistrar接口允许注解配置mybatis对mapper进行扫描,扫描到basePackage之后再根据MapperScannerConfigurer类来实现mybatis和spring的集成。源码如下:
public class MapperScannerRegistrar
implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
// ImportBeanDefinitionRegistrar接口的方法,解析完@Import注解后将被调用
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata
.getAnnotationAttributes(MapperScan.class.getName()));
// 如果@MapperScan注解的属性不为空则调用方法registerBeanDefinitions
// 去注册MapperScannerConfigurer类到Spring工厂中
if (mapperScanAttrs != null) {
registerBeanDefinitions(mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
}
void registerBeanDefinitions(AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
// 为MapperScannerConfigurer类创建一个Bean定义构造器
BeanDefinitionBuilder builder =
BeanDefinitionBuilder
.genericBeanDefinition(MapperScannerConfigurer.class);
// processPropertyPlaceHolders这里会默认设置为true,如果为true则会先从
// PropertyValues和environment中查找对应的basePackage和lazyInitialization
// 等属性,如果这些属性MapperScannerConfigurer类中配置了则去配置的属性
builder.addPropertyValue("processPropertyPlaceHolders", true);
// 下面则是从注解的属性中获得相应的属性,再放到builder的PropertyValues中
Class<? extends Annotation> annotationClass =
annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass =
annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
builder.addPropertyValue("nameGenerator",
BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass =
annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
builder.addPropertyValue("mapperFactoryBeanClass",
mapperFactoryBeanClass);
}
String sqlSessionTemplateRef =
annoAttrs.getString("sqlSessionTemplateRef");
if (StringUtils.hasText(sqlSessionTemplateRef)) {
builder.addPropertyValue("sqlSessionTemplateBeanName",
annoAttrs.getString("sqlSessionTemplateRef"));
}
String sqlSessionFactoryRef =
annoAttrs.getString("sqlSessionFactoryRef");
if (StringUtils.hasText(sqlSessionFactoryRef)) {
builder.addPropertyValue("sqlSessionFactoryBeanName",
annoAttrs.getString("sqlSessionFactoryRef"));
}
// 解析@MapperScan的value、basePackages和basePackageClasses属性
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()));
// 懒加载
String lazyInitialization = annoAttrs.getString("lazyInitialization");
if (StringUtils.hasText(lazyInitialization)) {
builder.addPropertyValue("lazyInitialization", lazyInitialization);
}
// 将前面获得的包路径设置成XX,XX,XX格式
builder.addPropertyValue("basePackage",
StringUtils.collectionToCommaDelimitedString(basePackages));
// 将bean定义注册到注册中心
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
// 处理@MapperScans注解,里面有多个@MapperScan注解
static class RepeatingRegistrar extends MapperScannerRegistrar {
@Override
public void registerBeanDefinitions(
AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScansAttrs = AnnotationAttributes
.fromMap(importingClassMetadata
.getAnnotationAttributes(MapperScans.class.getName()));
// 如果@MapperScans里面的@MapperScan不为空
if (mapperScansAttrs != null) {
AnnotationAttributes[] annotations =
mapperScansAttrs.getAnnotationArray("value");
// 依次传入@MapperScan调用registerBeanDefinitions进行解析注册
for (int i = 0; i < annotations.length; i++) {
registerBeanDefinitions(annotations[i], registry,
generateBaseBeanName(importingClassMetadata, i));
}
}
}
}
}
该类方法流程分析:
- 如果使用的注解是@MapperScan则调用MapperScannerRegistrar,注解是@MapperScans则调用MapperScannerRegistrar类中的RepeatingRegistrar类;
- 在被@Configuration注解的类进行处理时,会调用ImportBeanDefinitionRegistrar接口的方法,该方法会将注解中的值使用AnnotationAttributes.fromMap()方法转变成AnnotationAttributes对象,再传入本类中的registerBeanDefinitions方法;
- 在registerBeanDefinitions方法中,会创建MapperScannerConfigurer类的bean定义构造类,然后将注解中的markerInterface、mapperFactoryBeanClass、sqlSessionFactoryBeanName等属性一次添加进BeanDefinitionBuilder类中,并在最后将MapperScannerConfigurer的bean定义注册进BeanDefinitionRegistry,此过程相当于完成第四篇中applicationContext中对MapperScannerConfigurer类的配置,并将类配置以bean定义的形式注册进BeanDefinitionRegistry中;
- 后续的步骤和第六篇的MapperScannerConfigurer方式集成spring一样,以该类的回调函数为入口,扫描mapper接口,并配置其他的属性等。