一、@MapperScan注解解析
@MapperScan(basePackages = "com.ziroom.springboot.springbootsourcetest.mapper")
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
MapperScan注解包含了@Import注解,作用是加载MapperScannerRegistrar对象
@Import 在springboot中是非常重要的,主要作用是加载指定类到spring容器中
MapperScannerRegistrar,java 同时实现了ImportBeanDefinitionRegistrar接口,该接口的实现方法是用来注册BeanDefinitions的,配合@Import注解,会调用ImportBeanDefinitionRegistrar接口的方法registerBeanDefinitions
二、MapperScannerRegistrar实现
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
}
该部分主要是获取到注解**@MapperScan**
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
... //省略部分代码
// 2、设置属性
String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
if (StringUtils.hasText(sqlSessionTemplateRef)) {
builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
}
//3、设置扫描的包
List<String> basePackages = new ArrayList<>();
basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
.collect(Collectors.toList()));
//4、当前注解所在的包路径
if (basePackages.isEmpty()) {
basePackages.add(getDefaultBasePackage(annoMeta));
}
// 5、将当前封装的BeanDefinition对象注册
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
BeanDefinitionBuilder
该对象是spring中对对象的描述,包含了对象的所有信息
1、创建MapperScannerConfigurer对象的BeanDefinitionBuilder对象,该对象包含了mybatis以及整合spring的所有的配置信息
basePackage
sqlSessionFactory
sqlSessionTemplate
…
2、AnnotationMetadata annoMeta是对注解MapperScan的封装,后面很多的代码都是将annoMeta的数据封装到BeanDefinitionBuilder对象中,后面在实例化MapperScannerConfigurer对象时进行属性赋值
3、设置mapper扫描的包
4、如果在注解@MapperScan里没有设置扫描的包路径,则会默认使用注解所在包的路径
5、将当前封装的BeanDefinition对象注册
三、MapperScannerConfigurer的BeanDefinition加载
该类实现了接口BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法,这个方法是在BeanDefinition被注册后执行的
@Override
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);
...
// 关键步骤,对mapper进行扫描
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
该方法主要是为了构建ClassPathMapperScanner对象,扫描指定的mapper,这里关键步骤在最后一步
四、扫描mapper–ClassPathMapperScanner
BeanDefinitionHolder
BeanDefinition的包装类,包含了BeanDefinition和BeanName信息
1、类ClassPathMapperScanner继承了spring的类ClassPathBeanDefinitionScanner,重写了doScan方法
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// 调用的是父类ClassPathBeanDefinitionScanner的方法
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 {
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
2、在调用父类的doScan时,会扫描到所有的mapper,将这些mapper进行组装成BeanDefinitionHolder,在返回集合的同时,将BeanDefinition生成代理对象并且注册到容器中(这里不是实例,还是BeanDefinition对象)
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
... 省略部分代码
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
// 代理对象
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
// 注册
registerBeanDefinition(definitionHolder, this.registry);
}
return beanDefinitions;
}
最后返回扫描的所有mapper的BeanDefinitionHolder集合
3、在获取代理对象这里进行几轮的跳转调用,进入AOP相关代理封装
public static BeanDefinitionHolder createScopedProxy(BeanDefinitionHolder definition, BeanDefinitionRegistry registry, boolean proxyTargetClass) {
String originalBeanName = definition.getBeanName();
BeanDefinition targetDefinition = definition.getBeanDefinition();
String targetBeanName = getTargetBeanName(originalBeanName);
RootBeanDefinition proxyDefinition = new RootBeanDefinition(ScopedProxyFactoryBean.class);
proxyDefinition.setDecoratedDefinition(new BeanDefinitionHolder(targetDefinition, targetBeanName));
proxyDefinition.setOriginatingBeanDefinition(targetDefinition);
proxyDefinition.setSource(definition.getSource());
proxyDefinition.setRole(targetDefinition.getRole());
proxyDefinition.getPropertyValues().add("targetBeanName", targetBeanName);
if (proxyTargetClass) {
targetDefinition.setAttribute(AutoProxyUtils.PRESERVE_TARGET_CLASS_ATTRIBUTE, Boolean.TRUE);
} else {
proxyDefinition.getPropertyValues().add("proxyTargetClass", Boolean.FALSE);
}
proxyDefinition.setAutowireCandidate(targetDefinition.isAutowireCandidate());
proxyDefinition.setPrimary(targetDefinition.isPrimary());
if (targetDefinition instanceof AbstractBeanDefinition) {
proxyDefinition.copyQualifiersFrom((AbstractBeanDefinition)targetDefinition);
}
targetDefinition.setAutowireCandidate(false);
targetDefinition.setPrimary(false);
registry.registerBeanDefinition(targetBeanName, targetDefinition);
return new BeanDefinitionHolder(proxyDefinition, originalBeanName, definition.getAliases());
}
4、返回扫描后的mapper的BeanDefinitionHolder集合后,又对每一个BeanDefinitionHolder进行注入配置,因为在这之前只是记录了接口相关的信息,还需要注入实现类以及一些属性值
private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
AbstractBeanDefinition definition;
BeanDefinitionRegistry registry = getRegistry();
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (AbstractBeanDefinition) holder.getBeanDefinition();
boolean scopedProxy = false;
String beanClassName = definition.getBeanClassName();
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
definition.setBeanClass(this.mapperFactoryBeanClass); // 设置实现类
definition.setLazyInit(lazyInitialization);
if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {
definition.setScope(defaultScope);
}
}
}
关键一步,设置BeanClass为mapperFactoryBeanClass
五、Mapper实例化
在实例化接口代理对象时,实际上是实例化的MapperFactoryBean,该类的顶级父类是spring的DaoSupport,他实现了InitializingBean接口,也就是在对象被实例化的时候会执行afterPropertiesSet方法
public abstract class DaoSupport implements InitializingBean {
protected final Log logger = LogFactory.getLog(this.getClass());
public DaoSupport() {
}
public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
this.checkDaoConfig();
try {
this.initDao();
} catch (Exception var2) {
throw new BeanInitializationException("Initialization of DAO failed", var2);
}
}
protected abstract void checkDaoConfig() throws IllegalArgumentException;
protected void initDao() throws Exception {
}
}
这里通过模板模式去调用checkDaoConfig方法
在MapperFactoryBean类中实现了checkDaoConfig方法,这里主要是将扫描的mapper接口加入到myabtis中
@Override
protected void checkDaoConfig() {
super.checkDaoConfig();
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = getSqlSession().getConfiguration();
if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
try {
// 添加mapper
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();
}
}
}
this.mapperInterface 扫描到的class类对象
六、总结
mybatis和spring的整合实际上就是将mapper交给spring管理,也就是将mapper注入到IOC容器中
BeanDefinition是对扫描到的bean的描述,每个类都有一个BeanDefinition对象一一对应,spring中所有的Bean实例化都是通过该对象生成的
在整合的过程中会出现多种BeanDefinition的包装类,都是用来生产spring Bean描述对象BeanDefinition的
1、@MapperScan注解在被加载时,会触发@Import注解
2、触发MapperScannerRegistrar类的registerBeanDefinitions方法
3、在registerBeanDefinitions方法中会注册MapperScannerConfigurer对象,同时触发MapperScannerConfigurer注册后会触发注册通知方法postProcessBeanDefinitionRegistry
4、在postProcessBeanDefinitionRegistry方法中会创建ClassPathMapperScanner类,该类继承自spring的扫描类
5、ClassPathMapperScanner的scan方法会去扫描指定包下的mapper,同时将mapper的代理对象MapperFactoryBean注入
6、最后将mapper组装好的BeanDefinition对象进行注册,完成spring管理
7、后面的操作就和单独使用mybatis一样了,通过注解获取到mapper
七、补充
- 在这里我是用注解的形式讲解的,实际上还有XML的方式
- 在mybatis中我们使用到的类似于SqlSessionFactory这样的对象,在与spring整合时候是通过xml去配置的这些对象,而在springboot项目中是自动装配的