mybatis第四话 - 让我们一层一层来剥开mybatis的心,源码分析

22 篇文章 0 订阅
13 篇文章 0 订阅

人嘛,总会是这样。当某一个东西会用了,用的舒服了,然后多多少少都会想起了解它的原理。那么今天就来了!

前面什么单数据源,多数据源,高端用法,都是小菜。今天开始上硬货,准备好就发车了。

预先准备

  • 基于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,后面会用到

这里先说明一下根据某个类怎么找源码的入口

  1. static静态代码块
  2. 先直接看有没有@Bean,因为该类被加载时,@Bean标签肯定会被扫描。
  3. 看spring的各种回调,例如InitializingBean#afterPropertiesSet,ImportBeanDefinitionRegistrar#registerBeanDefinitions等等
  4. 当如果是接口,不知道走哪个实现类时,可以在接口上打断点,工具会告诉你走哪个
  5. 最后一点尤其重要,当不确定的情况下,疯狂断点保准没错

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

失之东隅,收之桑榆

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值