网上搜索了很多文章中,大部分只是提到mapper扫描步骤,但是生成mapper实例的过程,以及使用mapper时怎么用的没查到一篇文章整合的。然后本人能力有限,查找了很多文章,整理了这篇文章,有不对的大佬可以指出了。
参考文章:
Mybatis-Spring原理分析 – @MapperScan注解
springboot集成下,mybatis的mapper代理对象究竟是如何生成的
springboot中,mybatis的mapper接口是如何生成代理对象的?
有的文章看了,没有记录全。如有雷同,纯属copy。嘿嘿。
主要的思路就是:springboot 自动配置mybatis,扫描所有的mapper接口,修改其BeanDefinition的beanClass为MapperFactoryBean,spring会为mapper接口生成代理对象。使用mapper时,用的是代理对象的功能。
1.@Mapper方式
MybatisAutoConfiguration
为springboot自动配置mybatis的类,其中
MapperScannerRegistrarNotFoundConfiguration
为一种mapper扫描方式,如果没有配置
@MapperScan
注解,则这个配置类生效。
/**
* If mapper registering configuration or mapper scanning configuration not present, this configuration allow to scan
* mappers based on the same component-scanning path as Spring Boot itself.
*/
@org.springframework.context.annotation.Configuration
@Import(AutoConfiguredMapperScannerRegistrar.class)
@ConditionalOnMissingBean({ MapperFactoryBean.class, MapperScannerConfigurer.class })
public static class MapperScannerRegistrarNotFoundConfiguration implements InitializingBean {
@Override
public void afterPropertiesSet() {
logger.debug(
"Not found configuration for registering mapper bean using @MapperScan, MapperFactoryBean and MapperScannerConfigurer.");
}
}
AutoConfiguredMapperScannerRegistrar
组件主要是向容器注册
MapperScannerConfigurer
这个组件,主要扫描配置了
@Mapper
注解的接口
/**
* This will just scan the same base package as Spring Boot does. If you want more power, you can explicitly use
* {@link org.mybatis.spring.annotation.MapperScan} but this will get typed mappers working correctly, out-of-the-box,
* similar to using Spring Data JPA repositories.
*/
public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, EnvironmentAware, ImportBeanDefinitionRegistrar {
private BeanFactory beanFactory;
private Environment environment;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
builder.addPropertyValue("annotationClass", Mapper.class);
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)
.collect(Collectors.toSet());
if (propertyNames.contains("lazyInitialization")) {
// Need to mybatis-spring 2.0.2+
builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
}
if (propertyNames.contains("defaultScope")) {
// Need to mybatis-spring 2.0.6+
builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
}
// for spring-native
boolean injectSqlSession = environment.getProperty("mybatis.inject-sql-session-on-mapper-scan", Boolean.class,
Boolean.TRUE);
if (injectSqlSession && this.beanFactory instanceof ListableBeanFactory) {
ListableBeanFactory listableBeanFactory = (ListableBeanFactory) this.beanFactory;
Optional<String> sqlSessionTemplateBeanName = Optional
.ofNullable(getBeanNameForType(SqlSessionTemplate.class, listableBeanFactory));
Optional<String> sqlSessionFactoryBeanName = Optional
.ofNullable(getBeanNameForType(SqlSessionFactory.class, listableBeanFactory));
if (sqlSessionTemplateBeanName.isPresent() || !sqlSessionFactoryBeanName.isPresent()) {
builder.addPropertyValue("sqlSessionTemplateBeanName",
sqlSessionTemplateBeanName.orElse("sqlSessionTemplate"));
} else {
builder.addPropertyValue("sqlSessionFactoryBeanName", sqlSessionFactoryBeanName.get());
}
}
builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
}
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
private String getBeanNameForType(Class<?> type, ListableBeanFactory factory) {
String[] beanNames = factory.getBeanNamesForType(type);
return beanNames.length > 0 ? beanNames[0] : null;
}
}
2.@MapperScan方式
@MapperScan
向容器中注册
MapperScannerRegistrar
这个组件,主要是扫描指定包下面的
mapper
接口
,最终也是由
MapperScannerConfigurer
这个类起作用。
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {...}
MapperScannerRegistrar
类向容器中注册
MapperScannerConfigurer
类
@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);
***
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
3.MapperScannerConfigurer
上面两种方式最终都会由这个类来完成
mapper
的配置。现在来分析这个类。
主要这个方法:
@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);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
}
if (StringUtils.hasText(defaultScope)) {
scanner.setDefaultScope(defaultScope);
}
scanner.registerFilters();
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
主要是
ClassPathMapperScanner
这个类完成了
mapper
的
beandefintion
定义信息的修改
@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) {
AbstractBeanDefinition definition;
BeanDefinitionRegistry registry = getRegistry();
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (AbstractBeanDefinition) holder.getBeanDefinition();
boolean scopedProxy = false;
if (ScopedProxyFactoryBean.class.getName().equals(definition.getBeanClassName())) {
definition = (AbstractBeanDefinition) Optional
.ofNullable(((RootBeanDefinition) definition).getDecoratedDefinition())
.map(BeanDefinitionHolder::getBeanDefinition).orElseThrow(() -> new IllegalStateException(
"The target bean definition of scoped proxy bean not found. Root bean definition[" + holder + "]"));
scopedProxy = true;
}
String beanClassName = definition.getBeanClassName();
// the mapper interface is the original class of the bean
// but, the actual class of the bean is MapperFactoryBean
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
// 修改beanClass的类型
definition.setBeanClass(this.mapperFactoryBeanClass);
***
}
}
4.spring实例化mapper
主要看
AbstractBeanFactory
中关键的代码
// Create bean instance.
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, () -> {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
getSingleton()
这个方法最终会调到org.apache.ibatis.binding.MapperRegistry#addMapper 方法
private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
knownMappers.put(type, new MapperProxyFactory<>(type));
// It's important that the type is added before the parser is run
// otherwise the binding may automatically be attempted by the
// mapper parser. If the type is already known, it won't try.
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
spring this.getSingleton()这个方法获取的是原始bean,并不一定是我们想要的bean
初始化personMapper -> ((InitializingBean)bean).afterPropertiesSet(); bean = MapperFactoryBean
getObjectForBeanInstance() 这个方法最终会调到org.apache.ibatis.binding.MapperRegistry#getMapper这个方法
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
利用的是JDK的动态代理,InvocationHandler是MapperProxy,后续Mapper代理对象方法的执行都会先经过MapperProxy的invoke方法。
public class MapperProxyFactory<T> {
private final Class<T> mapperInterface;
private final Map<Method, MapperMethodInvoker> methodCache = new ConcurrentHashMap<>();
public MapperProxyFactory(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
public Class<T> getMapperInterface() {
return mapperInterface;
}
public Map<Method, MapperMethodInvoker> getMethodCache() {
return methodCache;
}
@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
}
到此,spring中就要了mapper实例对象(代理对象),然后就是使用。
5.使用mapper实例
@Test
void contextLoads() {
List<Person> people = personMapper.queryAll();
for (Person person : people) {
System.out.println("person = " + person);
}
}
实际使用的是MapperProxy代理对象,调用的是org.apache.ibatis.binding.MapperProxy#invoke方法。
这样一直走到原生sql查询