人嘛,总会是这样。当某一个东西会用了,用的舒服了,然后多多少少都会想起了解它的原理。那么今天就来了!
前面什么单数据源,多数据源,高端用法,都是小菜。今天开始上硬货,准备好就发车了。
预先准备
- 基于springboot 2.5.6版本,pom依赖贴这几个
<!--Mysql连接-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!--mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.0</version>
</dependency>
- 数据库连接
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://192.168.0.100:3306/demo_test?serverTimezone=Asia/Shanghai&characterEncoding=utf8&useSSL=false
username: root
password: 123456
源码分析
mybatis的源码可以分为三个阶段,初始化阶段、运行阶段、调用阶段。
1. 初始化阶段
- springboot的核心我觉得就是自动装配了,这里肯定少不了。再次复习一下自动装配的原理。
通过loadSpringFactories扫描jar包路径下的META-INF/spring.factories文件,从中找出key名为org.springframework.boot.autoconfigure.EnableAutoConfiguration对应的值,则为需要自动装配的类。
SpringFactoriesLoader
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
- 既然如此,直接找到
mybatis-spring-boot-autoconfigure-2.2.0.jar
下的META-INF/spring.factories
文件
# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration,\
org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
- 上面需要自动装配的就这两个类,看类名也知道走哪个了吧!直接来到
MybatisAutoConfiguration
类 - 注意这个类里面有两个@Bean 一个是sqlSessionFactory,一个是sqlSessionTemplate,后面会用到
这里先说明一下根据某个类怎么找源码的入口
- static静态代码块
- 先直接看有没有@Bean,因为该类被加载时,@Bean标签肯定会被扫描。
- 看spring的各种回调,例如
InitializingBean#afterPropertiesSet,ImportBeanDefinitionRegistrar#registerBeanDefinitions
等等 - 当如果是接口,不知道走哪个实现类时,可以在接口上打断点,工具会告诉你走哪个
- 最后一点尤其重要,当不确定的情况下,疯狂断点保准没错
1.1 MybatisAutoConfiguration类
1. 类注解分析
//标识该类是一个配置类
@org.springframework.context.annotation.Configuration
//必须有这两个class引入才加载
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
//容器中必须存在该数据源类,切实单例bean
@ConditionalOnSingleCandidate(DataSource.class)
//配置类
@EnableConfigurationProperties(MybatisProperties.class)
//初始化该类之后,自动装配这两个类
@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })
2. spring的回调ImportBeanDefinitionRegistrar,静态类AutoConfiguredMapperScannerRegistrar#registerBeanDefinitions
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (!AutoConfigurationPackages.has(this.beanFactory)) {
logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
return;
}
//英文意思是开始查找注解了@Mapper的mapper java文件
logger.debug("Searching for mappers annotated with @Mapper");
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
if (logger.isDebugEnabled()) {
packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg));
}
//这里是重点 向容易注册MapperScannerConfigurer类
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
//下面是传递的key名和参数
builder.addPropertyValue("processPropertyPlaceHolders", true);
//set annotationClass 值为 Mapper.class注解
builder.addPropertyValue("annotationClass", Mapper.class);
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
//然后用beanWarpper包装 看过IOC的都知道 等待容器初始化的类都是包装成beanWarpper的
BeanWrapper beanWrapper = new BeanWrapperImpl(MapperScannerConfigurer.class);
Set<String> propertyNames = Stream.of(beanWrapper.getPropertyDescriptors()).map(PropertyDescriptor::getName)
.collect(Collectors.toSet());
if (propertyNames.contains("lazyInitialization")) {
// Need to mybatis-spring 2.0.2+
builder.addPropertyValue("lazyInitialization", "${mybatis.lazy-initialization:false}");
}
if (propertyNames.contains("defaultScope")) {
// Need to mybatis-spring 2.0.6+
builder.addPropertyValue("defaultScope", "${mybatis.mapper-default-scope:}");
}
//向容器注册这个类
registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
}
3. Mapper扫描类MapperScannerConfigurer
//实现了这么多回调接口,入口应该很好找
public class MapperScannerConfigurer
implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
- 最终找到
BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry
回调
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
//注册时传的参数 Mapper.class
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);
}
//扫描规则,这里最终到
//addIncludeFilter(new AnnotationTypeFilter(this.annotationClass));
//this.includeFilters.add(includeFilter);
//也就是将Mapper.class加到一个list中
scanner.registerFilters();
//扫描 ###
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
- 接着看扫描方法
ClassPathMapperScanner#doScan
@Override
public Set<BeanDefinitionHolder> doScan(String... basePackages) {
//这里是到父类的ClassPathBeanDefinitionScanner的doScan()方法 先看这个 ###
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;
}
//父类ClassPathBeanDefinitionScanner#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());
String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
if (candidate instanceof AbstractBeanDefinition) {
postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
}
if (candidate instanceof AnnotatedBeanDefinition) {
AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
}
//在this.registry 没有该注册信息就包装成BeanDefinitionHolder
if (checkCandidate(beanName, candidate)) {
BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
definitionHolder =
AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
//添加到 beanDefinitions 可供后续代码修改bean注册信息
beanDefinitions.add(definitionHolder);
//进行注册
registerBeanDefinition(definitionHolder, this.registry);
}
}
}
return beanDefinitions;
}
//然后往下到 ClassPathScanningCandidateComponentProvider -> findCandidateComponents -> scanCandidateComponents()方法
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
Set<BeanDefinition> candidates = new LinkedHashSet<>();
try {
//CLASSPATH_ALL_URL_PREFIX = classpath*:
//resourcePattern = **/*.class
//加载main方法那层目录下的class类,
String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
resolveBasePackage(basePackage) + '/' + this.resourcePattern;
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
//......省略部分代码
try {
MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
//校验规则 满足就添加 ###
if (isCandidateComponent(metadataReader)) {
ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
sbd.setSource(resource);
if (isCandidateComponent(sbd)) {
if (debugEnabled) {
logger.debug("Identified candidate component class: " + resource);
}
candidates.add(sbd);
//省略部分代码
......
return candidates;
}
//有两个isCandidateComponent(metadataReader)
protected boolean isCandidateComponent(MetadataReader metadataReader) throws IOException {
for (TypeFilter tf : this.excludeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return false;
}
}
//前面通过scanner.registerFilters(); 添加的Mapper.class
for (TypeFilter tf : this.includeFilters) {
if (tf.match(metadataReader, getMetadataReaderFactory())) {
return isConditionMatch(metadataReader);
}
}
return false;
}
//isCandidateComponent(sbd)
//类上有注释 检查不是接口 不是闭包 不是抽象类
//如果是抽象类,但是里面有某个方法上面有@lookup注解,则也可以生成bean
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
AnnotationMetadata metadata = beanDefinition.getMetadata();
return (metadata.isIndependent() && (metadata.isConcrete() ||
(metadata.isAbstract() && metadata.hasAnnotatedMethods(Lookup.class.getName()))));
}
- 至此,
Set<BeanDefinition> candidates
应该都是带有@Mapper注解的类了,并将注册信息注册到容器中了,接下来分析processBeanDefinitions(beanDefinitions)
注册信息处理
//beanDefinitions @Mapper注解的类
private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
AbstractBeanDefinition definition;
BeanDefinitionRegistry registry = getRegistry();
for (BeanDefinitionHolder holder : beanDefinitions) {
definition = (AbstractBeanDefinition) holder.getBeanDefinition();
//。。。省略部分代码
//例如com.xxx.xxx.TestMapper
String beanClassName = definition.getBeanClassName();
//添加到通用的构造方法参数列表
definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
//mapper接口是bean的原始类
//但是,设置实际初始的类为mapperFactoryBeanClass = MapperFactoryBean.class;
//也就是加载这个注册信息的时候,实际加载的类为MapperFactoryBean类
//原始接口类名会当做构造方法传入
definition.setBeanClass(this.mapperFactoryBeanClass);
//......省略中间赋值的部分传参
//如果不是单例的注册信息 重新包装后移除注册表中的信息 重新注册
//因为在doScan扫描方法已注册 这段代码不会再走
//但修改里面的BeanDefinitionHolder bean依然生效,因为指向的是同一个内存地址
if (!definition.isSingleton()) {
BeanDefinitionHolder proxyHolder = ScopedProxyUtils.createScopedProxy(holder, registry, true);
if (registry.containsBeanDefinition(proxyHolder.getBeanName())) {
registry.removeBeanDefinition(proxyHolder.getBeanName());
}
registry.registerBeanDefinition(proxyHolder.getBeanName(), proxyHolder.getBeanDefinition());
}
}
}
到这里,项目中的mapper接口已经注册到容器中了,只不过beanName是对应的接口名,但实际上初始化的类已经替换成了MapperFactoryBean类
2. 运行阶段
回到最开始的入口类MybatisAutoConfiguration
,有两个@Bean需要加载sqlSessionFactory、sqlSessionTemplate
2.1 @Bean注解
- sqlSessionTemplate就不贴了,这里只贴下sqlSessionFactory
@Bean
@ConditionalOnMissingBean
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
factory.setDataSource(dataSource);
//一系列的初始化配置 这里省略吧
......
//这里是重点 到SqlSessionFactoryBean#getObject ##
return factory.getObject();
}
//SqlSessionFactoryBean#getObject里面 -> afterPropertiesSet -> buildSqlSessionFactory找到这里
for (Resource mapperLocation : this.mapperLocations) {
//this.mapperLocations为配置的mybatis.mapperLocations=classpath:mapper/*/*.xml
if (mapperLocation == null) {
continue;
}
try {
//解析对应的xml文件
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
//解析xml文件了 接下来走这里 ###
xmlMapperBuilder.parse();
}
加载SqlSessionFactory Bean的时候会扫描配置的xml目录下的文件,接下来到解析
2.2 xml文件解析 XMLMapperBuilder#parse
public void parse() {
if (!configuration.isResourceLoaded(resource)) {
//解析mapper节点 看这里 ###
configurationElement(parser.evalNode("/mapper"));
configuration.addLoadedResource(resource);
//接下来添加到MapperRegistry->Map<Class<?>, MapperProxyFactory<?>> knownMappers
bindMapperForNamespace();
}
parsePendingResultMaps();
parsePendingCacheRefs();
parsePendingStatements();
}
2.2.1 mapper节点解析
//xml文件里面的节点解析 想详细了解的自己继续深入
private void configurationElement(XNode context) {
try {
//mapper接口包名
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.isEmpty()) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
//添加到本地包名
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
//入参映射
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
//出参映射
resultMapElements(context.evalNodes("/mapper/resultMap"));
//通用sql
sqlElement(context.evalNodes("/mapper/sql"));
//解析sql语句了
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);
}
}
2.2.2 sql语句节点解析
buildStatementFromContext 一直往下到XMLStatementBuilder#parseStatementNode
public void parseStatementNode() {
//id
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
//select|insert|update|delete
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// Include Fragments before parsing
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
//参数类型
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
// Parse selectKey after includes and remove them.
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
//insert语句返回自增id的
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;
}
//其他参数 用过应该都还熟悉
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String resultType = context.getStringAttribute("resultType");
Class<?> resultTypeClass = resolveClass(resultType);
String resultMap = context.getStringAttribute("resultMap");
String resultSetType = context.getStringAttribute("resultSetType");
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
if (resultSetTypeEnum == null) {
resultSetTypeEnum = configuration.getDefaultResultSetType();
}
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
String resultSets = context.getStringAttribute("resultSets");
//最终添加到configuration里面的mappedStatements Map中
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
2.2.3 bindMapperForNamespace 绑定Mapper
- 再回到
XMLMapperBuilder#parse
继续分析bindMapperForNamespace
//包装并保存mapper接口
private void bindMapperForNamespace() {
//获取命名空间 即接口全路径
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
//添加类型 例如 interface com.xxx.xxx.TestMapper
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
// ignore, bound type is not required
}
//类型不为空且mapper不存在才添加
if (boundType != null && !configuration.hasMapper(boundType)) {
configuration.addLoadedResource("namespace:" + namespace);
//这里一直往下到 MapperRegistry 这里 ###
configuration.addMapper(boundType);
}
}
}
//MapperRegistry type mapper的namespace,即mapper接口的包名
public <T> void addMapper(Class<T> type) {
if (type.isInterface()) {
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
//保存到本地map 并且用MapperProxyFactory类包装 ###
knownMappers.put(type, new MapperProxyFactory<>(type));
//使用注解的sql进行解析
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
sqlsessionFactoryBean 加载完毕后,将mapper文件解析出来的信息封装成MappedStatement
保存到了Configuration#mappedStatements Map
里面
bindMapperForNamespace 获取到mapper文件的命名空间保存到Configuration#MapperRegistry类knownMappers Map
里面,key名为命名空间,即接口的全路径,value同时用MapperProxyFactory
包装key
2.3 Mybatis的偷天换日
在初始化阶段已经看到将所有的Mapper接口注册到容器中了,但BeanClass在ClassPathMapperScanner#processBeanDefinitions
被替换成了MapperFactoryBean.class
,那就来看看这个类有什么神器之处
- 首先看到的是
InitializingBean#afterPropertiesSet
回调
@Override
protected void checkDaoConfig() {
super.checkDaoConfig();
//this.mapperInterface 还记得吧 通过构造方法传入的mapper接口全路径名
notNull(this.mapperInterface, "Property 'mapperInterface' is required");
Configuration configuration = getSqlSession().getConfiguration();
//这里判断configuration里面的mapperRegistry#knownMappers Map中有没有这个值
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();
}
}
}
和XMLMapperBuilder#parse
中的bindMapperForNamespace
最终的目的是一个,其实经过断点可以得知解析会排在这个回调之前,也就是不会走这行代码
configuration.addMapper(this.mapperInterface);
- getObject() 在源码
FactoryBeanRegistrySupport#doGetObjectFromFactoryBean
调用,意思很明显,由自己掌控自己的实例对象
//也就是说在某个mapper接口初始化时,会调用该方法获取真正的实例对象
@Override
public T getObject() throws Exception {
//前面查看到过SqlSession bean 其实就是SqlSessionTemplate
//参数是mapper接口全路径名
return getSqlSession().getMapper(this.mapperInterface);
}
2.4 MapperRegistry#getMapper的真谛
- 一直往下找到这里
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
//前面bindMapperForNamespace添加的mapper接口
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
//这里才是返回的最终的实例化对象 ###
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
MapperProxyFactory#newInstance
public T newInstance(SqlSession sqlSession) {
//用MapperProxy在封装一层
final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
//生成动态代理 代理模式
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
2.5 运行阶段完成,有以下几点应该就懂了
1.初始化mapper接口的时候,实际上初始的是MapperFactoryBean类。只不过beanName还是原来的接口名
2.初始MapperFactoryBean时会调用getObject()方法创建代理类。得到的是代理对象MapperProxy类,也就是说我们在有@Autowired Mapper接口的时候实际上拿到的是MapperProxy代理对象
3.当我们得知实际上注入的Mapper接口是一个个MapperProxy代理对象时,学过动态代理的人都知道,代理类的调用应该走哪里吧
3 调用阶段
3.1 MapperProxy#invoke方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
if (Object.class.equals(method.getDeclaringClass())) {
return method.invoke(this, args);
} else {
//走这里没什么疑问吧 ###
return cachedInvoker(method).invoke(proxy, method, args, sqlSession);
}
} catch (Throwable t) {
throw ExceptionUtil.unwrapThrowable(t);
}
}
private MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
//如果缓存没有才更新
return MapUtil.computeIfAbsent(methodCache, method, m -> {
if (m.isDefault()) {
try {
if (privateLookupInMethod == null) {
return new DefaultMethodInvoker(getMethodHandleJava8(method));
} else {
return new DefaultMethodInvoker(getMethodHandleJava9(method));
}
} catch (IllegalAccessException | InstantiationException | InvocationTargetException
| NoSuchMethodException e) {
throw new RuntimeException(e);
}
} else {
//包装了一个内部代理类 PlainMethodInvoker
return new PlainMethodInvoker(new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}
});
} catch (RuntimeException re) {
Throwable cause = re.getCause();
throw cause == null ? re : cause;
}
}
//从上面得知包装的是PlainMethodInvoker
//接下来到PlainMethodInvoker里面的invoke方法
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
//上面得知mapperMethod = MapperMethod这个类 往这里走 ###
return mapperMethod.execute(sqlSession, args);
}
- MapperMethod#execute方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
switch (command.getType()) {
//这里就是各种sql执行类型了 主要了解查询SELECT
case INSERT: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.insert(command.getName(), param));
break;
}
case UPDATE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.update(command.getName(), param));
break;
}
case DELETE: {
Object param = method.convertArgsToSqlCommandParam(args);
result = rowCountResult(sqlSession.delete(command.getName(), param));
break;
}
//主要了解这个 其他的可以断点自行了解
case SELECT:
if (method.returnsVoid() && method.hasResultHandler()) {
executeWithResultHandler(sqlSession, args);
result = null;
} else if (method.returnsMany()) {
//因为断点时 查询的是多条数据 走这里
result = executeForMany(sqlSession, args);
} else if (method.returnsMap()) {
result = executeForMap(sqlSession, args);
} else if (method.returnsCursor()) {
result = executeForCursor(sqlSession, args);
} else {
Object param = method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(command.getName(), param);
if (method.returnsOptional()
&& (result == null || !method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
//......省略部分代码
return result;
}
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
List<E> result;
Object param = method.convertArgsToSqlCommandParam(args);
//有分页的情况下
if (method.hasRowBounds()) {
RowBounds rowBounds = method.extractRowBounds(args);
result = sqlSession.selectList(command.getName(), param, rowBounds);
} else {
//走这里 sqlSession是到SqlSessionTemplate ###
result = sqlSession.selectList(command.getName(), param);
}
// issue #510 Collections & arrays support
//返回值处理
if (!method.getReturnType().isAssignableFrom(result.getClass())) {
if (method.getReturnType().isArray()) {
return convertToArray(result);
} else {
return convertToDeclaredCollection(sqlSession.getConfiguration(), result);
}
}
return result;
}
3.2 SqlSessionTemplate#selectList方法
public <E> List<E> selectList(String statement, Object parameter) {
//看这个名字就不简单
return this.sqlSessionProxy.selectList(statement, parameter);
}
//果断找一下构造方法 @Bean new SqlSessionTemplate的
public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
PersistenceExceptionTranslator exceptionTranslator) {
notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
notNull(executorType, "Property 'executorType' is required");
this.sqlSessionFactory = sqlSessionFactory;
this.executorType = executorType;
this.exceptionTranslator = exceptionTranslator;
//代理类 执行的是SqlSessionInterceptor
this.sqlSessionProxy = (SqlSession) newProxyInstance(SqlSessionFactory.class.getClassLoader(),
new Class[] { SqlSession.class }, new SqlSessionInterceptor());
}
3.3 事务的核心类SqlSessionInterceptor
private class SqlSessionInterceptor implements InvocationHandler {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//获取一个新的sqlsession 为什么? 因为事务的存在,不同的事务是需要不同的session的
SqlSession sqlSession = getSqlSession(SqlSessionTemplate.this.sqlSessionFactory,
SqlSessionTemplate.this.executorType, SqlSessionTemplate.this.exceptionTranslator);
try {
//这里才是原始类的调用 SqlSession.selectList,
//这里SqlSession是接口,找默认实现 DefaultSqlSession 接下来走这里 ###
Object result = method.invoke(sqlSession, args);
//如果没有事务 直接做事务提交
if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
// force commit even on non-dirty sessions because some databases require
// a commit/rollback before calling close()
sqlSession.commit(true);
}
return result;
} catch (Throwable t) {
Throwable unwrapped = unwrapThrowable(t);
if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
// release the connection to avoid a deadlock if the translator is no loaded. See issue #22
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
sqlSession = null;
Throwable translated = SqlSessionTemplate.this.exceptionTranslator
.translateExceptionIfPossible((PersistenceException) unwrapped);
if (translated != null) {
unwrapped = translated;
}
}
throw unwrapped;
} finally {
if (sqlSession != null) {
//执行完毕直接关闭 session
closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
}
}
}
}
事务就不在这篇文章展开了,代码比较多,放在后面文章分析
3.4 sql执行
DefaultSqlSession#selectList
一直往下
private <E> List<E> selectList(String statement, Object parameter, RowBounds rowBounds, ResultHandler handler) {
try {
//取出sql语句等信息 statement/接口全路径
MappedStatement ms = configuration.getMappedStatement(statement);
//执行器 这里可能是代理类 会调用到代理的invoke方法 放在下章内容分析
//这里直接到最终的执行器吧 ###
return executor.query(ms, wrapCollection(parameter), rowBounds, handler);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error querying database. Cause: " + e, e);
} finally {
ErrorContext.instance().reset();
}
}
- 直接到最终的执行器这里了
BaseExecutor#query
@Override
public <E> List<E> query(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
//组装sql 和参数
BoundSql boundSql = ms.getBoundSql(parameter);
//默认是开启缓存的 创建缓存key
CacheKey key = createCacheKey(ms, parameter, rowBounds, boundSql);
//走这里 ###
return query(ms, parameter, rowBounds, resultHandler, key, boundSql);
}
BaseExecutor#query -> queryFromDatabase-> SimpleExecutor#doQuery
@Override
public <E> List<E> doQuery(MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
Statement stmt = null;
try {
Configuration configuration = ms.getConfiguration();
StatementHandler handler = configuration.newStatementHandler(wrapper, ms, parameter, rowBounds, resultHandler, boundSql);
//预处理
stmt = prepareStatement(handler, ms.getStatementLog());
//查询
return handler.query(stmt, resultHandler);
} finally {
closeStatement(stmt);
}
}
//prepareStatement
private Statement prepareStatement(StatementHandler handler, Log statementLog) throws SQLException {
Statement stmt;
//获取一个连接 后续分析
Connection connection = getConnection(statementLog);
//预处理sql和参数
stmt = handler.prepare(connection, transaction.getTimeout());
handler.parameterize(stmt);
return stmt;
}
- PreparedStatementHandler#query 执行
public <E> List<E> query(Statement statement, ResultHandler resultHandler) throws SQLException {
PreparedStatement ps = (PreparedStatement) statement;
//执行
ps.execute();
//查询流程到此结束了 处理返回结果 ###
return resultSetHandler.handleResultSets(ps);
}
3.5 返回值处理 handleResultSets
DefaultResultSetHandler#handleResultSets -> handleResultSet -> handleRowValues ->handleRowValuesForSimpleResultMap -> getRowValue
private Object getRowValue(ResultSetWrapper rsw, ResultMap resultMap, String columnPrefix) throws SQLException {
final ResultLoaderMap lazyLoader = new ResultLoaderMap();
//根据你的 resultType || resultMap 创建对应的接收对象 先了解这个 ###
Object rowValue = createResultObject(rsw, resultMap, lazyLoader, columnPrefix);
if (rowValue != null && !hasTypeHandlerForResultObject(rsw, resultMap.getType())) {
final MetaObject metaObject = configuration.newMetaObject(rowValue);
boolean foundValues = this.useConstructorMappings;
if (shouldApplyAutomaticMappings(resultMap, false)) {
//设置key Value 接下来走这里 ###
foundValues = applyAutomaticMappings(rsw, resultMap, metaObject, columnPrefix) || foundValues;
}
foundValues = applyPropertyMappings(rsw, resultMap, metaObject, lazyLoader, columnPrefix) || foundValues;
foundValues = lazyLoader.size() > 0 || foundValues;
rowValue = foundValues || configuration.isReturnInstanceForEmptyRow() ? rowValue : null;
}
return rowValue;
}
//createResultObject -> 一直往下
private Object createResultObject(ResultSetWrapper rsw, ResultMap resultMap, List<Class<?>> constructorArgTypes, List<Object> constructorArgs, String columnPrefix)
throws SQLException {
//获取返回值得类型 我这里是 resultType="Map"
final Class<?> resultType = resultMap.getType();
final MetaClass metaType = MetaClass.forClass(resultType, reflectorFactory);
final List<ResultMapping> constructorMappings = resultMap.getConstructorResultMappings();
if (hasTypeHandlerForResultObject(rsw, resultType)) {
return createPrimitiveResultObject(rsw, resultMap, columnPrefix);
} else if (!constructorMappings.isEmpty()) {
return createParameterizedResultObject(rsw, resultType, constructorMappings, constructorArgTypes, constructorArgs, columnPrefix);
} else if (resultType.isInterface() || metaType.hasDefaultConstructor()) {
//会走这个条件 返回是接口 或者返回的类型由默认的构造方法 ##
return objectFactory.create(resultType);
} else if (shouldApplyAutomaticMappings(resultMap, false)) {
return createByConstructorSignature(rsw, resultType, constructorArgTypes, constructorArgs);
}
throw new ExecutorException("Do not know how to create an instance of " + resultType);
}
//objectFactory.create(resultType) -> DefaultObjectFactory.resolveInterface
protected Class<?> resolveInterface(Class<?> type) {
Class<?> classToCreate;
if (type == List.class || type == Collection.class || type == Iterable.class) {
classToCreate = ArrayList.class;
} else if (type == Map.class) {
//所以最终得到的是个hashMap
classToCreate = HashMap.class;
} else if (type == SortedSet.class) { // issue #510 Collections Support
classToCreate = TreeSet.class;
} else if (type == Set.class) {
classToCreate = HashSet.class;
} else {
classToCreate = type;
}
return classToCreate;
}
DefaultResultSetHandler#applyAutomaticMappings
private boolean applyAutomaticMappings(ResultSetWrapper rsw, ResultMap resultMap, MetaObject metaObject, String columnPrefix) throws SQLException {
//获取查询结果中的字段key
//getUnmappedColumnNames -> loadMappedAndUnmappedColumnNames ->columnNames
//columnNames通过构造方法封装结果时录入的 类似与MVC的传参 下标和参数key的概念
List<UnMappedColumnAutoMapping> autoMapping = createAutomaticMappings(rsw, resultMap, metaObject, columnPrefix);
boolean foundValues = false;
if (!autoMapping.isEmpty()) {
for (UnMappedColumnAutoMapping mapping : autoMapping) {
//从结果集中取值 typeHandler转换类型
final Object value = mapping.typeHandler.getResult(rsw.getResultSet(), mapping.column);
if (value != null) {
foundValues = true;
}
if (value != null || (configuration.isCallSettersOnNulls() && !mapping.primitive)) {
// gcode issue #377, call setter on nulls (value is not 'found')
//赋值 ###
metaObject.setValue(mapping.property, value);
}
}
}
return foundValues;
}
MetaObject#setValue
//先看构造方法
private MetaObject(Object object, ObjectFactory objectFactory, ObjectWrapperFactory objectWrapperFactory, ReflectorFactory reflectorFactory) {
this.originalObject = object;
this.objectFactory = objectFactory;
this.objectWrapperFactory = objectWrapperFactory;
this.reflectorFactory = reflectorFactory;
if (object instanceof ObjectWrapper) {
this.objectWrapper = (ObjectWrapper) object;
} else if (objectWrapperFactory.hasWrapperFor(object)) {
this.objectWrapper = objectWrapperFactory.getWrapperFor(this, object);
} else if (object instanceof Map) {
//前面得知类型是Map 那肯定走这里了
this.objectWrapper = new MapWrapper(this, (Map) object);
} else if (object instanceof Collection) {
this.objectWrapper = new CollectionWrapper(this, (Collection) object);
} else {
this.objectWrapper = new BeanWrapper(this, object);
}
}
//MapWrapper#set
@Override
public void set(PropertyTokenizer prop, Object value) {
if (prop.getIndex() != null) {
Object collection = resolveCollection(prop, map);
setCollectionValue(prop, collection, value);
} else {
//直接map put了
//map 就是构造方法传入的hashMap this.map = map;
map.put(prop.getName(), value);
}
}
所有值赋值完成后返回的就是创建的map了,返回值的分析就到此结束了。
源码总结
1.初始化阶段
1.利用自动装配初始一个类MybatisAutoConfiguration,在静态类AutoConfiguredMapperScannerRegistrar实现了spring的回调,作为扫描mapper接口的入口MapperScannerConfigurer
2.找到的mapper接口注册到容器,将beanClass替换成了MapperFactoryBean类,也就是初始化Mapper接口时实际上在初始化MapperFactoryBean类
2.运行阶段
1.通过MybatisAutoConfiguration类中@Bean注解创建sqlsessionFactoryBean时扫描mapper文件,将Mapper文件中扫描出来的sql等信息保存到了Configuration#mappedStatements Map里面,将命名空间也就是接口信息保存到了Configuration#MapperRegistry类knownMappers Map中,key为接口全路径,value同时用MapperProxyFactory包装key
2.在MapperFactoryBean中实现了FactoryBean接口,实现了getObject方法,该方法的意思是实例化对象时由自己做主,这里返回的就是上面的MapperProxyFactory的包装类,调用类为MapperProxy。
3.在MybatisAutoConfiguration类中@Bean创建SqlSessionTemplate时,通过构造方法生成了一个代理的sqlSessionProxy,调用类为SqlSessionInterceptor
3.调用阶段
1.当容器初始化Mapper接口类时实际上得到的是MapperFactoryBean类对象,但该类实现了getObject方法,返回了最终的实例化对象,是一个代理类MapperProxy,也就是mapper接口实际上注入的是MapperProxy代理类
2.也就是会执行到MapperProxy#invoke类,然后在使用sqlSession时实际上是到代理类SqlSessionInterceptor,在该类完成了sqlSession的新建和复用以及事务提交和回滚
3.接下来才到真正的执行器executor,这里可能存在多层代理,插件的执行入口也在这里实现
4.最后在prepareStatement完成数据库连接getConnection,并执行sql,最终由handleResultSets处理结果集并返回
当然中间还有很多坑,例如,事务的开启,数据库怎么连接的,还有最重要的插件,由于内容过长就不放到本篇文章细说了。后面会慢慢填坑!
本文源码分析均是在无事务下debug分析的,后续文章会分析事务的整个流程,敬请期待!
以上就是本章的全部内容了。
上一篇:mybatis第三话 - mybatis的高端用法你会吗?
下一篇:mybatis第五话 - mybatis情同手足的插件之TypeHandler和Interceptor
失之东隅,收之桑榆