本文简单讲述mybatis整合到spring的自动配置流程。
首先,当我们引入mybatis-spring-boot-starter的时候,可以看到其pom.xml文件到中包含
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-autoconfigure</artifactId>
</dependency>
这个依赖就是自动配置的相关代码。在mybatis-spring-boot-autoconfigure
的META-INF目录下,可以看到spring.factories文件。
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
熟悉springboot starter原理的都知道这个文件。接下来,我们主要关注MybatisAutoConfiguration
这个类。该类有两个内部类AutoConfiguredMapperScannerRegistrar
和MapperScannerRegistrarNotFoundConfiguration
,MapperScannerRegistrarNotFoundConfiguration
上有@import
注解引用了AutoConfiguredMapperScannerRegistrar
。
AutoConfiguredMapperScannerRegistrar
实现了ImportBeanDefinitionRegistrar
接口的registerBeanDefinitions方法,在该方法中注册了一个MapperScannerConfigurer
的BeanDefinition。
而MapperScannerConfigurer
实现了BeanDefinitionRegistryPostProcessor
,可将额外的BeanDefinition注册到spring容器中。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
...
// 将额外的BeanDefinition注册到spring beanDefinition容器中
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
需要注意的是ClassPathMapperScanner
实例scanner的scan方法在其父类,但是调用的doScan方法在该类被重写了。
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
// 获取所有的Mapper,生成beanDefinition
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
// 将Mapper对应的beanDefinition注册到beanDefinition容器中
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
在doScan方法中,首先调用父类的doScan方法,根据配置的basePackages扫描所有的Mapper并生成beanDefinition的集合,接着调用了processBeanDefinitions(beanDefinitions)方法对这些beanDefinition进行处理。
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); // issue #59
definition.setBeanClass(this.mapperFactoryBeanClass);
...
}
}
该方法最主要的是将动作
- 将Mapper的接口加到BeanDefinition的构造器参数中
- 把BeanDefinition的beanClassName替换为MapperFactoryBean.class
以上流程,大致上完成了将Mapper注册到spring容器。
接下来讲讲MapperFactoryBean这个类
可以看到,该类实现了InitializingBean和FactoryBean两个接口。因此要重点关注InitializingBean的afterPropertiesSet方法和FactoryBean的getObject方法的实现。在DaoSupport类中,可以看到,afterPropertiesSet主要调用了checkDaoConfig方法。
@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 {
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();
}
}
}
而getObject方法的实现如下
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
跟踪checkDaoConfig方法中的configuration.addMapper(this.mapperInterface)代码,到MapperRegistry.addMapper方法。
public <T> void addMapper(Class<T> type) {
...
knownMappers.put(type, new MapperProxyFactory<>(type));
...
}
这个MapperProxyFactory
就是一个JDK动态代理的工厂类。
public class MapperProxyFactory<T> {
...
@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);
}
}
getObject方法最终将调用newInstance方法,获得一个代理对象。
再来看看MapperProxy
这个类,该类实现了InvocationHandler
类,因此主要看实现的invoke方法,这里就不在叙述了。