springboot整合mybatis原理

355 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

pom引入依赖

<!--mybatis与spring整合-->
<dependency>
  <groupId>org.mybatis</groupId>
  <artifactId>mybatis-spring</artifactId>
  <version>1.3.1</version>
</dependency>
<!--mybatis与spring-boot整合-->
<dependency>
  <groupId>org.mybatis.spring.boot</groupId>
  <artifactId>mybatis-spring-boot-starter</artifactId>
  <version>1.3.2</version>
</dependency>

springboot启动配置bean

@Configuration
@ImportResource(locations = { "classpath:spring/*" })
@PropertySource("classpath:config/datasource.properties")
@ComponentScan(basePackages = "org.gallant.spring")
@MapperScan("org.gallant.spring.transaction.mapper")
@EnableTransactionManagement
public class AppContext {
}

mybatis为了整合springboot开发了starter桥接包与实现包。桥接包中指定自动配置的实现提供者:provides: mybatis-spring-boot-autoconfigure,mybatis,mybatis-spring。查看mybatis自动配置类实现源码如下

@org.springframework.context.annotation.Configuration
@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })
@ConditionalOnBean(DataSource.class)
@EnableConfigurationProperties(MybatisProperties.class)
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
public class MybatisAutoConfiguration {
...//暂时仅关注注解部分
}

注册properties配置bean

EnableConfigurationProperties注解指定EnableConfigurationPropertiesImportSelector导入选择器导入ConfigurationPropertiesBeanRegistrar配置bean定义并注册至上下文、ConfigurationPropertiesBindingPostProcessorRegistrar将ConfigurationPropertiesBindingPostProcessor注册至上下文,ConfigurationPropertiesBindingPostProcessor将在配置bean初始化前绑定配置属性postProcessBeforeInitialization。配置bean类型为MybatisProperties,名称为mybatis-org.mybatis.spring.boot.autoconfigure.MybatisProperties(如果有前缀),配置MybatisProperties类的ConfigurationProperties注解指定配置的前缀为mybatis。

注册sqlSessionFactory bean

创建SqlSessionFactoryBean,根据properties配置factory,获取SqlSessionFactory bean

@Override
public SqlSessionFactory getObject() throws Exception {
  if (this.sqlSessionFactory == null) {
    afterPropertiesSet();
  }
  return this.sqlSessionFactory;
}
@Override
public void afterPropertiesSet() throws Exception {
  ...
  // 1.根据配置初始化Configuration
  //   1.x 根据mapperLocations解析mapper xml文件至configuration.getSqlFragments
  // 2. 根据Configuration配置构建SqlSessionFactory实例
  // return this.sqlSessionFactoryBuilder.build(configuration);
  this.sqlSessionFactory = buildSqlSessionFactory();
}

注册sqlSessionTemplate bean

注册Mapper bean

Mapper扫描

定义Mapper扫描注册类实现

// class : MybatisAutoConfiguration 的内部类
public static class AutoConfiguredMapperScannerRegistrar
      implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {

    private BeanFactory beanFactory;

    private ResourceLoader resourceLoader;

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

      logger.debug("Searching for mappers annotated with @Mapper");

      ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

      try {
        if (this.resourceLoader != null) {
          scanner.setResourceLoader(this.resourceLoader);
        }

        List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
        if (logger.isDebugEnabled()) {
          for (String pkg : packages) {
            logger.debug("Using auto-configuration base package '{}'", pkg);
          }
        }

        scanner.setAnnotationClass(Mapper.class);
        scanner.registerFilters();
        scanner.doScan(StringUtils.toStringArray(packages));
      } catch (IllegalStateException ex) {
        logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
      }
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
      this.beanFactory = beanFactory;
    }

    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
      this.resourceLoader = resourceLoader;
    }
  }

导入Mapper扫描注册bean

MapperScan注解指定导入MapperScannerRegistrar。import注解在spring上下文刷新时导入指定的bean。具体逻辑可以查看ConfigurationClassParser.processImports方法。 MapperScannerRegistrar源码分析

  1. 获取MapperScan注解元数据信息在这里插入图片描述
  2. 创建ClassPathMapperScanner实例
  3. 根据注解注册过滤器scanner.registerFilters(),annotationClass、markerInterface可以指定需要扫描的类型,如果未指定默认接收所有接口acceptAllInterfaces;默认会增加排除exclude package-info.java
  4. 根据basePackage扫描doScan

执行扫描

  1. 调用父类扫描ClassPathBeanDefinitionScanner.doScan。
  2. 遍历basePackages目录,将匹配的源文件封装为ScannedGenericBeanDefinition类型,返回所有匹配的bean定义。spring扫描bean的目录顺序:Application所在目录、@ComponentScan注解配置目录、@MapperScan注解配置目录
  3. 将扫描结果注册至上下文等待初始化

初始化Mapper bean

AbstractApplicationContext.finishBeanFactoryInitialization执行上下文中非懒惰加载bean的初始化 ConstructorResolver.autowireConstructor构造Mapper实例,Mapper bean定义类型为org.mybatis.spring.mapper.MapperFactoryBean,候选的构造器列表如下 在这里插入图片描述 按照选中的构造器(有class为入参的构造器),使用CglibSubclassingInstantiationStrategy父类SimpleInstantiationStrategy.instantiate创建动态代理对象,如果getMethodOverrides为空则调用BeanUtils._instantiateClass,否则调用子类重写的_instantiateWithMethodInjection方法

constructorToUse

在这里插入图片描述

argsToUse

在这里插入图片描述

构建后的beanWrapper

在这里插入图片描述

注入属性populateBean

  1. 各种InstantiationAwareBeanPostProcessor后置处理动作
  2. autowire注入模式默认为类型getResolvedAutowireMode=AUTOWIRE_BY_TYPE(2)
  3. 待注入的属性列表addToConfig、sqlSessionFactory、sqlSessionTemplate,从工厂中解析依赖resolveDependency,获取依赖bean缓存至MutablePropertyValues待注入
  4. 解析sqlSessionFactory,从工厂获取bean
  5. 执行依赖注入,即@Autowired或者@Resource MyMapper mapper在这里插入图片描述
  6. 从工厂中获取依赖类型的bean在这里插入图片描述
  7. mapper依赖描述在这里插入图片描述
  8. 待注入的属性列表
  9. 在这里插入图片描述
  10. 注入applyPropertyValues

初始化对象initializeBean

返回bean对象

在这里插入图片描述

MapperFactoryBean获取Mapper

MapperFactoryBean实现了spring的FactoryBean接口,通过spring容器回调(AbstractBeanFactory.getObjectForBeanInstance)getObject构建Mapper返回

@Override
public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}

SqlSessionTemplate会话模板获取Mapper

@Override
public <T> T getMapper(Class<T> type) {
  return getConfiguration().getMapper(type, this);
}

Configuration配置获取Mapper

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }

MapperRegistry注册中心获取Mapper

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  // 从已知Mapper类型缓存中获取代理工厂,此处是一个map,key为mapper类型,value为代理工厂
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  if (mapperProxyFactory == null) {
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  }
  try {
    // mapper代理工厂创建mapper实例
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}

Mapper代理工厂创建Mapper实例,可以看到使用的是jdk自带的动态代理

// 类:MapperProxyFactory
protected T newInstance(MapperProxy<T> mapperProxy) {
  return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
}
public T newInstance(SqlSession sqlSession) {
  final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
  return newInstance(mapperProxy);
}

Mapper动态代理实现

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  ...
  final MapperMethod mapperMethod = cachedMapperMethod(method);
  return mapperMethod.execute(sqlSession, args);
}
private MapperMethod cachedMapperMethod(Method method) {
  MapperMethod mapperMethod = methodCache.get(method);
  if (mapperMethod == null) {
    // 根据配置文件、接口、方法创建Mapper方法命令
    mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
    methodCache.put(method, mapperMethod);
  }
  return mapperMethod;
}

MapperMethod实例

// 类:MapperMethod
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
  this.command = new SqlCommand(config, mapperInterface, method);
  this.method = new MethodSignature(config, mapperInterface, method);
}

public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  switch (command.getType()) {
    case INSERT: {
  	Object param = method.convertArgsToSqlCommandParam(args);
      result = rowCountResult(sqlSession.insert(command.getName(), param));
      break;
    }
    ...
    default:
      throw new BindingException("Unknown execution method for: " + command.getName());
  }
  if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
    throw new BindingException("Mapper method '" + command.getName() 
        + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
  }
  return result;
}
  
// MapperMethod的内部类SqlCommand
public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
  final String methodName = method.getName();
  final Class<?> declaringClass = method.getDeclaringClass();
  // 根据配置文件解析Mapped语句
  MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
      configuration);
  ...
}

// MapperMethod的内部类SqlCommand
private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
    Class<?> declaringClass, Configuration configuration) {
  String statementId = mapperInterface.getName() + "." + methodName;
  if (configuration.hasStatement(statementId)) {
    return configuration.getMappedStatement(statementId);
  } else if (mapperInterface.equals(declaringClass)) {
    return null;
  }
  // 递归调用实现的父接口
  for (Class<?> superInterface : mapperInterface.getInterfaces()) {
    if (declaringClass.isAssignableFrom(superInterface)) {
      MappedStatement ms = resolveMappedStatement(superInterface, methodName,
          declaringClass, configuration);
      ...
    }
  }
  return null;
}

配置文件缓存的语句MappedStatement 在这里插入图片描述