目录
- 主配置类如下
- 先看看@MapperScan 源码
- MapperScannerRegistrar
- MapperScannerConfigurer
- 扫描包
- MapperFactoryBean:返回动态代理对象
- 生成动态代理对象
- 简单图示
主配置类如下
@SpringBootApplication
@MapperScan("com.jarvis.mybatis.mapper") //扫描com.jarvis.mybatis.mapper包下的mapper接口
@EnableCaching
public class MybatisApplication {
public static void main(String[] args) {
SpringApplication.run(MybatisApplication.class, args);
}
}
先看看@MapperScan 源码
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//通过MapperScannerRegistrar.class的registerBeanDefinitions方法将某个注册类(MapperScannerConfigurer)的BeanDefinition注册到spring容器
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
...
String[] basePackages() default {};
...
}
>>>启动主配置类,跟踪源码
MapperScannerRegistrar
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//(图1)解析@MapperScan注解信息,将MapperScan中的属性封装到一个AnnotationAttributes类型的对象中
AnnotationAttributes mapperScanAttrs = AnnotationAttributes
.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
if (mapperScanAttrs != null) {
// 如果mapperScanAttrs不为空,则调用如下方法进行注册bean定义
registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
generateBaseBeanName(importingClassMetadata, 0));
}
}
//注册bean定义!!!
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
//创建构造器,用于生成MapperScannerConfigurer的BeanDefinition
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
//builder创建bean定义的一系列步骤
builder.addPropertyValue("processPropertyPlaceHolders", true);
...
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
//(图2)
// 1. builder.getBeanDefinition():使用构造器生成MapperScannerConfigurer的bean定义
// 2. registry.registerBeanDefinition:将这个bean定义注册到spring容器
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
}
图1:将MapperScan中的属性封装到一个AnnotationAttributes类型的对象中
图2:生成并注册MapperScannerConfigurer的bean定义
总结:这一步主要是生成一个MapperScannerConfigurer的BeanDefinition,并注册到容器中。
MapperScannerConfigurer
一、先来看继承关系
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware
public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor
说明MapperScannerConfigurer
1. 本质是一个BeanFactoryPostProcessor,拥有方法postProcessBeanFactory,在BeanDefinition生成之后、创建原始对象之前执行
2. 是一个BeanDefinitionRegistryPostProcessor(顾名思义,注册BeanDefinition的后置处理器)
定义了方法postProcessBeanDefinitionRegistry,但是在什么时候执行?
很简单,只需要在MapperScannerConfigurer的这两个方法上打个断点debug一下,就可以知道。
经过debug,可知,先执行了postProcessBeanDefinitionRegistry,然后执行postProcessBeanFactory。
结论:在MapperScannerConfigurer的BeanDefinition生成之后、创建原始对象之前,会先执行postProcessBeanDefinitionRegistry把扫描包下的mapper接口注册到容器中
二、来看看postProcessBeanDefinitionRegistry
@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));
}
//扫描规则过滤
scanner.registerFilters();
//真正去扫描指定包路径下的bean定义信息,会先去调用父类ClassPathBeanDefinitionScanner.scan方法
//(图3) 扫描路径
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
图3:扫描包路径
扫描包
1. ClassPathBeanDefinitionScanner.scan方法
public int scan(String... basePackages) {
int beanCountAtScanStart = this.registry.getBeanDefinitionCount();
//跳到ClassPathMapperScanner.doScan方法
doScan(basePackages);
// Register annotation config processors, if necessary.
if (this.includeAnnotationConfig) {
AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
}
return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
}
2. ClassPathMapperScanner.doScan
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//通过父类的doScan方法扫描
//(图4)扫描结果
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(即beanDefinitions不为空),则处理这些mapper的BeanDefinition
processBeanDefinitions(beanDefinitions);
}
return beanDefinitions;
}
图4. 扫描结果
3. processBeanDefinitions:处理bean定义
/**
* 传入的参数:扫描包下的mapper接口的BeanDefinition
*/
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
GenericBeanDefinition definition;
//循环我们扫描出的mapper的bean定义,并进行“加工”
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (GenericBeanDefinition) holder.getBeanDefinition();
//获取bean的类名称 如:com.jarvis.mybatis.mapper.BookMapper
String beanClassName = definition.getBeanClassName();
LOGGER.debug(() -> "Creating MapperFactoryBean with name '" + holder.getBeanName() + "' and '" + beanClassName
+ "' mapperInterface");
/**
由于mapper是接口,不能被实例化,
所以这里使用setBeanClass将BeanDefinition中的beanClass属性替换为MapperFactoryBean.class --> 偷天换日
即所有的mapper的BeanDefinition经过这一步后,类型beanClass都是MapperFactoryBean
MapperFactoryBean实现了FactoryBean接口,这个接口会提供一个getObject()方法,
当调用getBean方法时,实际内部调用getObject()方法让我们来返回Mapper接口的代理对象。
因为真正要实例化的对象是MapperFactoryBean,这个类中有一个空的构造方法和带一个参数的构造方法(参数为mapperInterface,即动态代理需要的接口),
并且后面要通过jdk代理生成这个对象的动态代理,所以使用的是带一个参数的构造方法传入接口。
getConstructorArgumentValues().addGenericArgumentValue(beanClassName)
将beanClassName--mapper接口名称传入了BeanDefinition的constructorArgumentValues属性中,
注:constructorArgumentValues属性用于创建原始bean对象的时候根据参数的类型和个数使用对应的构造器实例化。
后面ioc容器实例化MapperFactoryBean的时候,
会将beanClassName传入到MapperFactoryBean中构造方法的mapperInterface参数,用于之后jdk动态代理
*/
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName);
definition.setBeanClass(this.mapperFactoryBeanClass);
//定义了private Class<? extends MapperFactoryBean> mapperFactoryBeanClass = MapperFactoryBean.class;
...
if (!explicitFactoryUsed) {
LOGGER.debug(() -> "Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
/**
将bean定义的注入模式改为ByType,用于之后注入SqlSessionFactory
*/
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
}
//设置懒加载
definition.setLazyInit(lazyInitialization);
}
}
至此,已经将扫描包下定义的mapper接口的BeanDefinition生成、加工并注册到spring容器中,之后的步骤就是根据beanFactory中的beanDefinitionMap的bean定义进行生成原始对象
MapperFactoryBean:返回动态代理对象
public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
private Class<T> mapperInterface;
private boolean addToConfig = true;
public MapperFactoryBean() {
// intentionally empty
}
//在对mapper接口的bean定义加工时在constructorArgumentValues属性中传入了mapper的接口名,
//故此会使用该构造方法实例化bean
public MapperFactoryBean(Class<T> mapperInterface) {
this.mapperInterface = mapperInterface;
}
...
/**
* 这里返回jdk代理生成的mapper接口的动态代理对象 跳到SqlSessionTemplate.getMapper
*/
@Override
public T getObject() throws Exception {
return getSqlSession().getMapper(this.mapperInterface);
}
...
}
生成动态代理对象
1. SqlSessionTemplate.getMapper
public <T> T getMapper(Class<T> type) {
return getConfiguration().getMapper(type, this);
}
2. MybatisMapperRegistry.getMapper
@Override
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//生成一个代理对象的工厂
final MybatisMapperProxyFactory<T> mapperProxyFactory = (MybatisMapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MybatisPlusMapperRegistry.");
}
try {
//这里返回了代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
注:
我这里使用的是MybatisPlus,代码和Mybatis是一样的,只是名字不一样,Mybatis中是MapperRegistry类
3. MybatisMapperProxyFactory
//jdk动态代理
protected T newInstance(MybatisMapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
final MybatisMapperProxy<T> mapperProxy = new MybatisMapperProxy<>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
简单图示