前提:
已经熟悉mybatis机制,或者熟悉源码原理等等。
比如mapper最后是一个mapperProxyBean()
开始
基于spring提供的接口,ImportBeanDefinitionRegistrar
接口介绍:
- ImportBeanDefinitionRegistrar类只能通过其他类@Import的方式来加载,通常是启动类或配置类。(在mybatis 通过Mapper Scan 注解实现)
- 使用@Import,如果括号中的类是ImportBeanDefinitionRegistrar的实现类,则会调用接口方法,将其中要注册的类注册成bean。
- 实现该接口的类拥有注册bean的能力。
官方说明:
/**
* Interface to be implemented by types that register additional bean definitions when
* processing @{@link Configuration} classes. Useful when operating at the bean definition
* level (as opposed to {@code @Bean} method/instance level) is desired or necessary.
*
* <p>Along with {@code @Configuration} and {@link ImportSelector}, classes of this type
* may be provided to the @{@link Import} annotation (or may also be returned from an
* {@code ImportSelector}).
*
* <p>An {@link ImportBeanDefinitionRegistrar} may implement any of the following
* {@link org.springframework.beans.factory.Aware Aware} interfaces, and their respective
* methods will be called prior to {@link #registerBeanDefinitions}:
* <ul>
* <li>{@link org.springframework.context.EnvironmentAware EnvironmentAware}</li>
* <li>{@link org.springframework.beans.factory.BeanFactoryAware BeanFactoryAware}
* <li>{@link org.springframework.beans.factory.BeanClassLoaderAware BeanClassLoaderAware}
* <li>{@link org.springframework.context.ResourceLoaderAware ResourceLoaderAware}
* </ul>
*
* <p>Alternatively, the class may provide a single constructor with one or more of
* the following supported parameter types:
* <ul>
* <li>{@link org.springframework.core.env.Environment Environment}</li>
* <li>{@link org.springframework.beans.factory.BeanFactory BeanFactory}</li>
* <li>{@link java.lang.ClassLoader ClassLoader}</li>
* <li>{@link org.springframework.core.io.ResourceLoader ResourceLoader}</li>
* </ul>
*
* <p>See implementations and associated unit tests for usage examples.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
* @see Import
* @see ImportSelector
* @see Configuration
*/
可以看下这篇文章的介绍死磕源码系列【ImportBeanDefinitionRegistrar源码详解】
这是spring提供的一个默认的bean注册接口,容器启动的时候会调用这个接口,且接口两个方法会注册其中的bean定义。 同时 会吧BeanDefinitionRegistry
这东西交出来。
在这个接口 注册了一个bean MapperScannerConfigurer
这是一个BeanFactoryPostProcessor。
我们知道 其他框架整合spring 基本都是使用BeanFactoryPostProcessor
这个类只有一个方法
/**
* Modify the application context's internal bean definition registry after its
* standard initialization. All regular bean definitions will have been loaded,
* but no beans will have been instantiated yet. This allows for adding further
* bean definitions before the next post-processing phase kicks in.
* @param registry the bean definition registry used by the application context
* @throws org.springframework.beans.BeansException in case of errors
*/
void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
so,可以通过这个方法注册bean。
看看mybatis 对这个方法做了什么?
- 代码
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
// 扫描器,继承自spring ClassPathBeanDefinitionScanner
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// 设置类的部分属性,大部分是与mybatis 有关,与spring 扫描bean 无关
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);
}
// 注册拦截器,影响spring 扫描结果
scanner.registerFilters();
// 开始扫描,真正实现 doScan
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
方法调用了一个扫描方法,且将注册器对象传给他就结束了。
扫描器是spring的实现
- Mapper 扫描器 ClassPathMapperScanner 分析
看看doScan ()
@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;
}
发现他拿到了父类扫描的bean 定义 然后调用了processBeanDefinitions
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
definition.setBeanClass(this.mapperFactoryBeanClass);
definition.getPropertyValues().add("addToConfig", this.addToConfig);
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) {
}
definition.getPropertyValues().add("sqlSessionTemplate",
new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
explicitFactoryUsed = true;
} else if (this.sqlSessionTemplate != null) {
if (explicitFactoryUsed) {
}
definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
explicitFactoryUsed = true;
}
if (!explicitFactoryUsed) {
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
definition.setLazyInit(lazyInitialization);
if (scopedProxy) {
continue;
}
if (ConfigurableBeanFactory.SCOPE_SINGLETON.equals(definition.getScope()) && defaultScope != null) {
definition.setScope(defaultScope);
}
这些bean 定义对象都被做了增强。class 设置了factoryBean。~~~
要知道spring对factoryBean的处理是getBean的时候会去factory的getObject 方法获取的
如果想要获取factoryBean本身,需要添加&符号。
因此,在getMapper的时候,是调用的MapperFactoryBean
的Getobject
看看getObject
@Override
public T getObject() throws Exception {
// 实际就是从sqlsession 中 mapperResister 中获取mapper.
return getSqlSession().getMapper(this.mapperInterface);
}
public SqlSession getSqlSession() {
return this.sqlSessionTemplate;
}
// sqlTemplate 其实就是sqlSession
public class SqlSessionTemplate implements SqlSession, DisposableBean {}
- 那么是如何将之注册成BeanDefinition 的呢?
这要依靠ClassPathMapperScanner
的父类。
因为刚刚看到在执行完doscan内没有做任何的register的操作,doScan 只是坐了一些beanDef的增强,那么这个beanDef 肯定就是在父类中被注册了。
往上追一下代码:
//org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan
// 看看 父类的scan、
Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
// 父类扫描过程.
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
// 扫描所有的基础包
for (String basePackage : basePackages) {
// 获取所有可以使用的beanDef
Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
// 单独操作每一个beanDef
for (BeanDefinition candidate : candidates) {
// 删除部分代码,不影响看主流程
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
// 这是最终返回的beanDefs 即我们子类可以操作的defintion 对象
beanDefinitions.add(definitionHolder);
// 注册 bean 定义对象
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
大概就这样吧, 语言比较凌乱,记录一个思路。如果想自己实现一套mybatis 和spring整合的框架大体 思路也是这样
- 思考一个问题?
为什么doscan 中会扫描mapper ? 不是什么类都能扫描? 默认是只扫描类的!
在源代码中看到:
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider#scanCandidateComponents
if (isCandidateComponent(sbd)) {
candidates.add(sbd);
}
而当前this 是: this = > classpathmapperscanner
因此只要方法 isCandidateComponent
返回true 该类就会被扫描到
因此重写方法即可。
mb 的实现:
@Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
}
第一个判断是是否是接口?
第二个判断只要不是静态内部类都返回true。
因此,我们可以接口里边套接口,,测试是可以被读取的。比较弱智,除了写的时候省事阅读性并不好。