原文网址:Spring(SpringBoot)--Mybatis源码对FactoryBean的应用_IT利刃出鞘的博客-CSDN博客
简介
本文分析Mybatis是如何应用Spring的FactoryBean的。
本文分析的版本:mybatis-spring-2.0.4.jar、spring-framework-5.2.7.RELEASE。
总体流程
初始化
- 在配置类标注@MapperScan("Mapper接口所在包路径")
- @MapperScan上边有:@Import(MapperScannerRegistrar.class)。向Spring注入此类。
- 创建Mapper扫描器的配置器(MapperScannerConfigurer)
- MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar,会被Spring自动回调此接口的registerBeanDefinitions方法。
- MapperScannerRegistrar#registerBeanDefinitions方法
- 构建MapperScannerConfigurer并注册进Spring容器。
- MapperScannerRegistrar#registerBeanDefinitions方法
- MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar,会被Spring自动回调此接口的registerBeanDefinitions方法。
- 创建扫描器,调用其scan方法。
- MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,会被Spring自动回调此接口的postProcessBeanDefinitionRegistry方法。
- postProcessBeanDefinitionRegistry方法:
- 创建一个自定义的扫描器:ClassPathMapperScanner(继承自Spring的ClassPathBeanDefinitionScanner),调用其scan方法,即:ClassPathMapperScanner#scan
- ClassPathMapperScanner#scan
- 此方法未覆写,是调用父类(ClassPathBeanDefinitionScanner)的实现
- 即:ClassPathBeanDefinitionScanner#scan
- 调用到:ClassPathBeanDefinitionScanner#doScan
- 此方法被ClassPathMapperScanner覆写,所以调用到:ClassPathMapperScanner#doScan
- 此方法未覆写,是调用父类(ClassPathBeanDefinitionScanner)的实现
- postProcessBeanDefinitionRegistry方法:
- MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,会被Spring自动回调此接口的postProcessBeanDefinitionRegistry方法。
- 扫描你指定的包路径下的所有Mapper接口,并转换为BeanDefinition
- ClassPathMapperScanner#doScan
- ClassPathMapperScanner#processBeanDefinitions
- 扫描你指定的包路径下的所有Mapper接口,并转换为BeanDefinition。
- 设置每一个BeanDefinition的BeanClass为:MapperFactoryBean.class(此类实现了FactoryBean接口)
- 之后实例化时会调用这个FactoryBean的getObject()方法来构建一个代理Mapper对象。这是将Mapper接口转变成一个对象交给Spring管理的关键。
- 通过definition.getPropertyValues().add()方法,传入该BeanDefinition代表的接口。
- 将所有的BeanDefinition通过上边步骤设置之后,全部注册到BeanFactory中,由BeanFactory对这些FactoryBean进行管理。
- ClassPathMapperScanner#processBeanDefinitions
- ClassPathMapperScanner#doScan
- 注入时,实例化Mapper的代理类(refresh时,完成对注入对象的实例化)
- MapperFactoryBean#getObject返回Mapper的代理类:MapperProxy.class。
- MapperProxy.class里有invoke方法,使用时会走这个方法。
实例化
- 在使用或者获取这些bean的时候,Spring首先获取你要使用的接口类型。
- 遍历当前容器内所有的bean逐个对比,当有匹配的直接返回。但是,因为Mapper接口还并没有被实例化,所以没有找到,所以在遍历到FactoryBean的时候,会调用getObjectType方法,将返回值与你要使用的接口类型作比对。
- 当 FactoryBean的返回类型匹配的时候,Spring会调用FactoryBean的getObject方法将对象创建出来。
- 创建过程中,通过之前传入的接口,做jdk动态代理,完成MyBatis的代理逻辑。
- 对象创建完成后,通过isSingleton方法的返回值判断,如果是单例对象,就将该对象缓存起来。并返回。
1.配置类标注@MapperScan("XXX")
2.创建MapperScannerConfigurer
对应方法:MapperScannerRegistrar#registerBeanDefinitions
概述
构建类型为MapperScannerConfigurer的BeanDefinition并注册进Spring容器。
流程
refresh() //AbstractApplicationContext
invokeBeanFactoryPostProcessors(beanFactory); //AbstractApplicationContext
invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors()); //PostProcessorRegistrationDelegate
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); //PostProcessorRegistrationDelegate
postProcessor.postProcessBeanDefinitionRegistry(registry); //PostProcessorRegistrationDelegate
processConfigBeanDefinitions(registry); //ConfigurationClassPostProcessor.class
......
registerBeanDefinitions //MapperScannerRegistrar
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
}
String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
if (StringUtils.hasText(sqlSessionTemplateRef)) {
builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
}
String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
if (StringUtils.hasText(sqlSessionFactoryRef)) {
builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
}
List<String> basePackages = new ArrayList();
basePackages.addAll((Collection)Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll((Collection)Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll((Collection)Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName).collect(Collectors.toList()));
if (basePackages.isEmpty()) {
basePackages.add(getDefaultBasePackage(annoMeta));
}
String lazyInitialization = annoAttrs.getString("lazyInitialization");
if (StringUtils.hasText(lazyInitialization)) {
builder.addPropertyValue("lazyInitialization", lazyInitialization);
}
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
3.扫描Mapper接口,转为BeanDefinition
对应方法:MapperScannerConfigurer#postProcessBeanDefinitionRegistry
概述
- 创建一个自定义的扫描器ClassPathMapperScanner(继承自Spring的ClassPathBeanDefinitionScanner),它扫描你传入的包路径下的所有的接口,并转换为BeanDefinition
- 遍历上一步转化的BeanDefinition,修改他们的BeanClass为MapperFactoryBean类(实现FactoryBean接口),为后面的设置动态代理打下基础。
流程

调用到本方法的流程
refresh() //AbstractApplicationContext
invokeBeanFactoryPostProcessors(beanFactory); //AbstractApplicationContext
//PostProcessorRegistrationDelegate
invokeBeanFactoryPostProcessors(beanFactory, this.getBeanFactoryPostProcessors());
invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry); //PostProcessorRegistrationDelegate
postProcessor.postProcessBeanDefinitionRegistry(registry); //PostProcessorRegistrationDelegate
postProcessBeanDefinitionRegistry //MapperScannerConfigurer.class
postProcessBeanDefinitionRegistry //MapperScannerConfigurer
scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ",; \t\n")); //MapperScannerConfigurer
scan(String... basePackages) //ClassPathBeanDefinitionScanner
doScan(basePackages); //ClassPathBeanDefinitionScanner
postProcessBeanDefinition((AbstractBeanDefinition)candidate, beanName); //ClassPathBeanDefinitionScanner
definition.setBeanClass(this.mapperFactoryBeanClass); //ClassPathBeanDefinitionScanner
这里的this.mapperFactoryBeanClass就是:MapperFactoryBean。
4.注入时:创建Mapper的代理类
概述
一般情况下,我们Controller会使用@Autowired注入Service,而Service会默认注入Mapper,此时就会
MapperFactoryBean#getObject返回Mapper的代理类:MapperProxy.class。
MapperProxy.class里有invoke方法,使用时会走这个方法。
流程
refresh() //AbstractApplicationContext
finishBeanFactoryInitialization(beanFactory); //AbstractApplicationContext
beanFactory.preInstantiateSingletons(); //DefaultListableBeanFactory
getBean() //DefaultListableBeanFactory
......
getObject() //MapperFactoryBean.class
this.getSqlSession().getMapper(this.mapperInterface); //MapperFactoryBean.class
......
getMapper(Class type, SqlSession sqlSession) //MapperRegistry.class
getConfiguration().getMapper(type, this) //SqlSessionTemplate.class
// mybatisMapperRegistry为MybatisMapperRegistry类型
mybatisMapperRegistry.getMapper(type, sqlSession); // MybatisConfiguration.class
getMapper(Class type, SqlSession sqlSession) //MybatisMapperRegistry.class
MybatisMapperRegistry继承MapperRegistry
MybatisMapperRegistry#getMapper具体如下:
getMapper(Class type, SqlSession sqlSession) //MybatisMapperRegistry.class
mapperProxyFactory.newInstance(sqlSession) //MybatisMapperRegistry.class
newInstance(SqlSession sqlSession) // MybatisMapperProxyFactory.class
// MybatisMapperProxyFactory.class(下边所有代码都在这里)
MapperProxy mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
newInstance(mapperProxy);
Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
MapperProxy
public class MapperProxy<T> implements InvocationHandler, Serializable {
private static final long serialVersionUID = -4724728412955527868L;
private static final int ALLOWED_MODES = 15;
private static final Constructor<Lookup> lookupConstructor;
private static final Method privateLookupInMethod;
private final SqlSession sqlSession;
private final Class<T> mapperInterface;
private final Map<Method, MapperProxy.MapperMethodInvoker> methodCache;
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperProxy.MapperMethodInvoker> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
return Object.class.equals(method.getDeclaringClass())
? method.invoke(this, args)
: this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
// 省略其他代码
}
5.调用
概述
我们一般在使用时,直接@Autowired注入一个Mapper接口,然后调用方法即可。
注入Mapper:
上边“注入时:创建Mapper的代理类”,已经说明了,注入时会返回包含MapperProxy的代理。
调用Mapper方法:
会调用到MapperProxy#invoke
- 创建实例来执行方法。(有DefaultMethodInvoker、PlainMethodInvoker两种类)
- 调用MapperMethod#execute方法
流程
invoke //MapperProxy
Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
追踪后者
cachedInvoker(Method method) //MapperProxy
new MapperProxy.PlainMethodInvoker(new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()));
它再调用invoke(proxy, method, args, this.sqlSession)
invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) //MapperProxy.PlainMethodInvoker
mapperMethod.execute(sqlSession, args); //MapperProxy.PlainMethodInvoker
execute(SqlSession sqlSession, Object[] args) //MapperMethod
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
Object param;
switch(this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
其他网址
FactoryBean简介以及Mybatis-Spring应用
Spring中FactoryBean的使用_goodluckwj的博客-CSDN博客
spring之手写mybatis-spring_yangyanping20108的博客-CSDN博客
【Mybatis源码】Mybatis如何为mapper接口生成代理对象--CSDN博客
二、MyBatis Mapper Bean初始化深度解析 - 奋斗人生 - OSCHINA - 中文开源技术交流社区
FactoryBean简介以及Mybatis-Spring应用
带你跳出源码地狱,从原理上理解MyBatis对Spring源码的扩展实现 - 知乎