springboot整合mybatis原理

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
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章