本篇博客是接着上一篇博客的《Mybatis初始化加载流程————Mapper接口注册》,不过跟上一篇博客没有太多的代码上的承接关系。先看一下工程的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="user1" class="com.beans.User">
<property name="userName" value="zhuyuqiang"/>
</bean>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="name" value="mysql"/>
<property name="url" value="jdbc:mysql://127.0.0.1:3306/world"/>
<property name="username" value="root"/>
<property name="password" value="zh4y4q5ang"/>
<property name="driverClassName" value="com.mysql.jdbc.Driver"/>
</bean>
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"/>
<property name="mapperLocations" value="classpath:mybatis/*.xml"/>
<property name="typeAliasesPackage" value="com.entities"/>
<property name="configLocation" value="classpath:config/configuration.xml"/>
</bean>
<bean id="mapperScannerConfigurer" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.interfaces"/>
</bean>
</beans>
在本篇博客要分析的内容中,在源码追踪的时候只需要关注MapperScannerConfigurer开始就可以了。先看一下这个类的继承关系:
一共继承了四个接口,主要看一下实现的postProcessBeanDefinitionRegistry方法。
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
//这个registry其实就是BeanFactory,在注册相关Bean信息的时候需要用到
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.registerFilters();
//整个MapperScannerConfigurer在配置文件只是指定了this.basePackage的值
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
由于在bean定义的时候,MapperScannerConfigurer中只有basePackage被赋值了,看一下scan方法:
org.mybatis.spring.mapper.ClassPathMapperScanner.java
@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;
}
先看一下父类的doScan的方法内容:
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) {
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
for (BeanDefinition candidate : candidates) {
ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
candidate.setScope(scopeMetadata.getScopeName());
//生成bean名称,以类名小写开头作为bean名称
//在这里就生成了beanName,beanName为cityMapper
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
//对beanDefinition进行填充一些默认值
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
if (checkCandidate(beanName, candidate)) {
//使用BeanDefinitionHolder将BeanDefinition封装起来,内部包含了beanName和beanDefinition。
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
beanDefinitions.add(definitionHolder);
//开始将BeanDefinitionHolder注册到registry中
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
下面就是保存beanDefinition的操作了。。。。
//向BeanDefinitionRegistry的实现类中注册BeanDefinition
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
//使用一个工具类完成definitionHolder的保存
BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}
public static void registerBeanDefinition(
BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
throws BeanDefinitionStoreException {
// Register bean definition under primary name.
String beanName = definitionHolder.getBeanName();
//现在以beanName为key将整个beanDefinition保存起来。
registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());
// Register aliases for bean name, if any.
String[] aliases = definitionHolder.getAliases();
if (aliases != null) {
for (String alias : aliases) {
registry.registerAlias(beanName, alias);
}
}
}
看到这里,关于接口的注册已经分析了一半了。已经在BeanFactory中添加了一个beanName为cityMapper的beanDefinition了,现在需要指明这个beanName对应的beanClass到底是为哪种具体的类型了。
现在看一下processBeanDefinitions方法:
//针对beandefinition设置beanClass:this.mapperFactoryBean.getClass()
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
//传入的beanDefinitions就是在父类中分析生成的所有beanDefinition的集合
for (BeanDefinitionHolder holder : beanDefinitions) {
//开始对集合内的BeanDefinition进行单独处理
definition = (GenericBeanDefinition) holder.getBeanDefinition();
//开始获取BeanClass信息了,在这里可以获取的名称只是接口的名称,并不是BeanClass
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
//重点,设置了BeanClass为MapperFactoryBean的class,即设置BeanClass为:new MapperFactoryBean<T>().getClass()
//MapperFactoryBean类实现了FactoryBean接口,在根据beanName获取对象的时候会调用getObject方法
definition.setBeanClass(this.mapperFactoryBean.getClass());
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);
}
}
}
所以我们使用cityMapper作为BeanName的时候,在获取实体对象的时候会通过MapperFactoryBean对象。关于接口注册到这里大致算是分析完了,代码中都添加了注释,应该还算是容易理解的。在下一篇的博客中,将会分析一下,在根据beanName是如何获取到接口的动态代理的。