一、mybatis和spring的集成主要是集成什么?
mybatis和spring的集成主要是2点,一是将mybatis的MapperProxy代理对象交给spring管理,由spring负责其全生命周期。二是使用spring的声明式事务。
二、MapperPoxy对象的生成过程
本文使用一个很简单的UserDO类搭建一个简单的Spingboot+mybatis的demo,来解释集成过程:
UserDO.java
package com.wp.spring.mybatis.pojo;
public class UserDO {
private Long id;
private String name;
private Integer age;
private String email;
//getter和setter方法省略
}
创建一个UserMapper接口:
package com.wp.spring.mybatis.mapper;
import com.wp.spring.mybatis.pojo.UserDO;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import java.util.List;
public interface UserMapper {
@Select("select * from mbp_user")
@Results(id = "userDO", value = {
@Result(property = "id", column = "mbp_id", id = true),
@Result(property = "name", column = "mbp_name"),
@Result(property = "age", column = "mbp_age"),
@Result(property = "email", column = "mbp_email")
})
List<UserDO> selectAll();
}
Springboot的启动类:
package com.wp.spring.mybatis;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan("com.wp.spring.mybatis.mapper")
public class WpSpringMybatisApplication {
public static void main(String[] args) {
SpringApplication.run(WpSpringMybatisApplication.class);
}
}
2.1 @MapperScan
需要注意的是,springboot的启动类WpSpringMybatisApplication上有一个@MapperScan注解,这个注解上描述了项目中MapperInterface的包路径(备注:MapperScan注解支持包路径的通配符)
让我们进一步看下@ScanMapper里面到底有什么
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
.......
}
可以看到在@MapperScan注解上,还有一个@Import注解,其值为MapperScannerRegistrar。
2.2 MapperScannerRegistrar.class
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
/**
* {@inheritDoc}
*
* @deprecated Since 2.0.2, this method not used never.
*/
@Override
@Deprecated
public void setResourceLoader(ResourceLoader resourceLoader) {
// NOP
}
/**
* {@inheritDoc}
*/
@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));
}
}
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
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"));
}
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()));
if (basePackages.isEmpty()) {
basePackages.add(getDefaultBasePackage(annoMeta));
}
String lazyInitialization = annoAttrs.getString("lazyInitialization");
if (StringUtils.hasText(lazyInitialization)) {
builder.addPropertyValue("lazyInitialization", lazyInitialization);
}
String defaultScope = annoAttrs.getString("defaultScope");
if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {
builder.addPropertyValue("defaultScope", defaultScope);
}
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
MapperScannerRegistrar主要作用是构建MapperScannerConfigurer的BeanDefinition,并且将其注册到Spring的类定义注册中心中。
2.3 MapperScannerConfigurer
接下来来看看MapperScannerConfigurer的作用是什么。MapperScannerConfigurer集成自BeanDefinitionRegistryPostProcessor(作用是:将一些额外的bean定义注册到注册中心中)
ClassPathMapperScanner.class
/**
* 调用父类的doScan方法来搜索并注册所有的候选mapperInterface,
* 然后将他们设置为MapperFactoryBean
*/
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
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;
}
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
for (BeanDefinitionHolder holder : beanDefinitions) {
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);
//将definition的beanClass设置为MapperFactoryBean.class
definition.setBeanClass(this.mapperFactoryBeanClass);
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);
}
definition.setLazyInit(lazyInitialization);
}
}
/**
* 扫描basePackages下的MapperInterface,并生成其对应的BeanDefinition,然后注册
**/
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Assert.notEmpty(basePackages, "At least one base package must be specified");
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
for (String basePackage : basePackages) {
// 找到basePackage的MapperInterface,并生成其对应的BeanDefinition
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//将MapperInterface的BeanDefinition注册到注册中心去
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
}
else {
return scanCandidateComponents(basePackage);
}
}
综合来看,MapperScannerConfigurer的作用就是发现@ScanMapper定义中的basePackage中的MapperInterface,并构建它们的beanDefinition,设置beanClass为MapperFactoryBean.class,然后将它们注册到注册中心中。
2.4 MapperFactoryBean.class
MapperFactoryBean继承了2个接口,分别是InitializingBean和FactoryBean。
其中InitializingBean的主要作用是将MapperInterface的ProxyMapper注册到Congfiguration.mapperRegistry中。
同时MapperFactoryBean继承自FactoryBean,是一个工厂Bean,其getObject方法就是生成了一个ProxyMapper代理对象。
至此,就完成了spring发现、注册、初始化MapperInterface的过程。