MyBatis 之 @Mapper 扫描和 实例化到 Spring
从 Spring-Boot 自动配置类可以看出,是用 MapperScannerConfigurer 扫描 @Mapper 并在 MyBatis 中生成代理对象,然后注册到 Spring 容器。
MapperScannerConfigurer
可以看出,是一个 BeanDefinitionRegistryPostProcessor,那么它肯定是用来扫描类,最终注册 bean 到 Spring 容器的。
java
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
// 这个类的用法可以参考 Spring-Boot 中的博客:ClassPathBeanDefinitionScanner 自定义注解扫描器
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);
}
/**
* 这个扫描器注册了拦截 @Mapper 注解的拦截器。
* 注解 @Mapper 类是在 AutoConfiguredMapperScannerRegistrar 类中设置的,然后传到这里面的。
*/
scanner.registerFilters();
/**
* 扫描所有 @Mapper 接口,并注册为 Spring 中的 BeanDefinition
*/
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
}
ClassPathMapperScanner.scan()
java
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
// Spring 的 ClassPathBeanDefinitionScanner.doScan()
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 {
// 扫描到 Mapper 接口的 BeanDefinition 后,在这里做进一步处理
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
processBeanDefinitions(beanDefinitions);
this.mapperFactoryBeanClass = org.mybatis.spring.mapper.MapperFactoryBean
所以,每个 Mapper 接口的 BeanDefinition 实际上是 MapperFactoryBean 类型的,而 MapperFactoryBean 实现了 FactoryBean, 因此,生成 Mapper 接口的代理对象的生成实际上主要是 MapperFactoryBean.getObject(); 方法。
有关 Spring 中的 FactoryBean 可以参考文章: Spring-Boot 之 FactoryBean
java
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();
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); // issue #59
/**
* this.mapperFactoryBeanClass = org.mybatis.spring.mapper.MapperFactoryBean
* 所以,每个 Mapper 接口的 BeanDefinition 实际上是 MapperFactoryBean 类型的,
* 而 MapperFactoryBean 实现了 FactoryBean,因此,生成 Mapper 接口的代理对象实际上主要是 MapperFactoryBean.getObject(); 方法。
*/
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
// Attribute for MockitoPostProcessor
// https://github.com/mybatis/spring-boot-starter/issues/475
definition.setAttribute(FACTORY_BEAN_OBJECT_TYPE, beanClassName);
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);
if (scopedProxy) {
continue;
}
if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {
definition.setScope(defaultScope);
}
if (!definition.isSingleton()) {
BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
registry.removeBeanDefinition(proxyHolder.getBeanName());
}
registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
}
}
}
MapperFactoryBean.getObject();
在这里,就看到了我们熟悉的 MyBatis 中的 SqlSession 对象了,这里就通过 SqlSession 生成 Mapper 接口的代理对象,最后放到 Spring 容器中。
java
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
}
那么,getSqlSession().getMapper(this.mapperInterface) 是如何生成代理对象的呢?接着看。
getMapper
java
public class SqlSessionTemplate implements SqlSession, DisposableBean {
@Override
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
}
public class Configuration {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return mapperRegistry.getMapper(type, sqlSession);
}
}
public class MapperRegistry {
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 可以看到,从 MyBatis 中的 knownMappers 容器中,根据 Mapper.class 获取到一个 MapperProxyFactory 对象。
// 为什么这里可以拿到 MapperProxyFactory?因为在建立 MyBatis 映射关系时,放进去的。
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 在这里,创建了 Mapper 接口的代理对象。实际上全都是 MapperProxy 类型的
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
}
public class MapperProxyFactory<T> {
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
/**
* 调到这里,创建了一个 MapperProxy 对象, MapperProxy<T> implements InvocationHandler,因此,它是一个动态代理的增强器,
* 当方法调用 Mapper 接口中的方法时,就会调用增强器 MapperProxy 的 invoke 方法。
*/
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
// 这里创建了 Mapper 接口的代理对象。
return newInstance(mapperProxy);
}
}
至此,应用程序启动完成,所有 MyBatis 生成的的 @Mapper 接口的代理对象都被 Spring 容器所管理。