Spring下Mybatis加载调用过程

一 SqlSessionFactoryBean加载
1.SqlSessionFactoryBean.afterPropertiesSet()

实现了InitializingBean,spring加载时会自动加载afterPropertiesSet方法

public void afterPropertiesSet() throws Exception {
  notNull(dataSource, "Property 'dataSource' is required");
  notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
  this.sqlSessionFactory = buildSqlSessionFactory();
}
2.SqlSessionFactoryBean.buildSqlSessionFactory()

​ 主要是加载Configuration,详请见下

3.SqlSessionFactoryBuilder.build()
public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}
二 Configuration加载
1.设置别名typeAliases
if (hasLength(this.typeAliasesPackage)) {
  String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
      ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  for (String packageToScan : typeAliasPackageArray) {
    configuration.getTypeAliasRegistry().registerAliases(packageToScan);
    if (this.logger.isDebugEnabled()) {
      this.logger.debug("Scanned package: '" + packageToScan + "' for aliases");
    }
  }
}

if (!isEmpty(this.typeAliases)) {
  for (Class<?> typeAlias : this.typeAliases) {
    configuration.getTypeAliasRegistry().registerAlias(typeAlias);
    if (this.logger.isDebugEnabled()) {
      this.logger.debug("Registered type alias: '" + typeAlias + "'");
    }
  }
}
2.设置插件plugins
if (!isEmpty(this.plugins)) {
  for (Interceptor plugin : this.plugins) {
    configuration.addInterceptor(plugin);
    if (this.logger.isDebugEnabled()) {
      this.logger.debug("Registered plugin: '" + plugin + "'");
    }
  }
}
3.设置类型处理器
if (hasLength(this.typeHandlersPackage)) {
  String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
      ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
  for (String packageToScan : typeHandlersPackageArray) {
    configuration.getTypeHandlerRegistry().register(packageToScan);
    if (this.logger.isDebugEnabled()) {
      this.logger.debug("Scanned package: '" + packageToScan + "' for type handlers");
    }
  }
}

if (!isEmpty(this.typeHandlers)) {
  for (TypeHandler<?> typeHandler : this.typeHandlers) {
    configuration.getTypeHandlerRegistry().register(typeHandler);
    if (this.logger.isDebugEnabled()) {
      this.logger.debug("Registered type handler: '" + typeHandler + "'");
    }
  }
}
4.根据configLocation加载Configuration
XMLConfigBuilder xmlConfigBuilder = null;
if (this.configLocation != null) {
  xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
  configuration = xmlConfigBuilder.getConfiguration();
} else {
  if (this.logger.isDebugEnabled()) {
    this.logger.debug("Property 'configLocation' not specified, using default MyBatis Configuration");
  }
  configuration = new Configuration();
  configuration.setVariables(this.configurationProperties);
}

加载mybatis-config.xml,没有的话则使用默认配置

5.事务管理器配置
if (this.transactionFactory == null) {
  this.transactionFactory = new SpringManagedTransactionFactory();
}
6.环境配置
Environment environment = new Environment(this.environment, this.transactionFactory, this.dataSource);
configuration.setEnvironment(environment);
7.设置DatabaseIdProvider
if (this.databaseIdProvider != null) {
  try {
    configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
  } catch (SQLException e) {
    throw new NestedIOException("Failed getting a databaseId", e);
  }
}
8.mapper.xml载入
if (!isEmpty(this.mapperLocations)) {
  for (Resource mapperLocation : this.mapperLocations) {
    if (mapperLocation == null) {
      continue;
    }

    try {
      XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
          configuration, mapperLocation.toString(), configuration.getSqlFragments());
      xmlMapperBuilder.parse();
    } catch (Exception e) {
      throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
    } finally {
      ErrorContext.instance().reset();
    }

    if (this.logger.isDebugEnabled()) {
      this.logger.debug("Parsed mapper file: '" + mapperLocation + "'");
    }
  }
} else {
  if (this.logger.isDebugEnabled()) {
    this.logger.debug("Property 'mapperLocations' was not specified or no matching resources found");
  }
}
三 Mapper注入Spring容器
1.MapperScannerConfigurer.postProcessBeanDefinitionRegistry()

实现了BeanDefinitionRegistryPostProcessor接口

Spring加载时会自动加载postProcessBeanDefinitionRegistry方法

将配置的Mapper包路径下的Mapper注入Spring容器

  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }

    Scanner scanner = new Scanner(beanDefinitionRegistry);
    scanner.setResourceLoader(this.applicationContext);

    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }
2.MapperFactoryBean.getObject()

对于FactoryBean,我们通过getObject()方法来获得实体。

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

俄罗斯套娃

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 调用
  return mapperRegistry.getMapper(type, sqlSession);
}
4.MapperRegistry.getMapper()

俄罗斯套娃

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    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);
    }
  }
5.MapperProxyFactory.newInstance()

真正实例化Mapper返回

@SuppressWarnings("unchecked")
protected T newInstance(MapperProxy<T> mapperProxy) {
    // 返回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调用
1.MapperProxy.invoke()

执行代理类的invoke方法

实际调用MapperMethod执行获取返回结果

  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    if (Object.class.equals(method.getDeclaringClass())) {
      return method.invoke(this, args);
    }
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
2.MapperProxy.cachedMapperMethod()
private MapperMethod cachedMapperMethod(Method method) {
    //缓存调用过得方法
  MapperMethod mapperMethod = methodCache.get(method);
  if (mapperMethod == null) {
    mapperMethod = new MapperMethod(mapperInterface, method, sqlSession.getConfiguration());
    methodCache.put(method, mapperMethod);
  }
  return mapperMethod;
}
3.MapperMethod构造方法

这个地方会校验Configuration中是否有对应的MapperStatment

也就是我们的老朋友Invalid bound statement (not found)

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

public SqlCommand(Configuration configuration, Class<?> declaringInterface, Method method) throws BindingException {
  name = declaringInterface.getName() + "." + method.getName();
  // 根据stamentId寻找configuration中的mapperStatment
  final MappedStatement ms;
  try {
    ms = configuration.getMappedStatement(name);
  } catch (Exception e) {
    throw new BindingException("Invalid bound statement (not found): " + name, e);
  }
  type = ms.getSqlCommandType();
  if (type == SqlCommandType.UNKNOWN) {
    throw new BindingException("Unknown execution method for: " + name);
  }
}
4.MapperMethod.execute()

根据不同的SQL语法及返回结果的数量,执行不同的方法

public Object execute(SqlSession sqlSession, Object[] args) {
  Object result;
  // 匹配不同的sql,INSERT UPDATE DELETE SELECT
  if (SqlCommandType.INSERT == command.getType()) {
    Object param = method.convertArgsToSqlCommandParam(args);
    result = rowCountResult(sqlSession.insert(command.getName(), param));
  } else if (SqlCommandType.UPDATE == command.getType()) {
    Object param = method.convertArgsToSqlCommandParam(args);
    result = rowCountResult(sqlSession.update(command.getName(), param));
  } else if (SqlCommandType.DELETE == command.getType()) {
    Object param = method.convertArgsToSqlCommandParam(args);
    result = rowCountResult(sqlSession.delete(command.getName(), param));
  } else if (SqlCommandType.SELECT == command.getType()) {
    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 {
      Object param = method.convertArgsToSqlCommandParam(args);
      result = sqlSession.selectOne(command.getName(), param);
    }
  } else {
    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;
}
5.SqlSession.selectOne()

其他同理

public <T> T selectOne(String statement, Object parameter) {
  // Popular vote was to return null on 0 results and throw exception on too many.
  List<T> list = this.<T>selectList(statement, parameter);
  if (list.size() == 1) {
    return list.get(0);
  } else if (list.size() > 1) {
    throw new TooManyResultsException("Expected one result (or null) to be returned by selectOne(), but found: " + list.size());
  } else {
    return null;
  }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Spring整合Mybatis的源码分析可以分为以下几个步骤: 1. 创建Spring容器并配置文件。在Spring Boot中,可以通过@SpringBootApplication注解来创建Spring容器,并在配置文件中配置Mybatis相关的属性。 2. 创建Mybatis的SqlSessionFactory。Spring Boot会自动配置Mybatis的SqlSessionFactory,通过读取配置文件中的数据源信息和Mybatis的配置信息,创建SqlSessionFactory对象。 3. 注册Mybatis的Mapper接口。Spring Boot会自动扫描项目中的Mapper接口,并将其注册到Spring容器中。 4. 创建Mapper代理对象。Spring Boot使用Mybatis的MapperFactoryBean来创建Mapper接口的代理对象。在创建代理对象时,会使用SqlSessionFactory来创建SqlSession,并将SqlSession注入到Mapper接口中。 5. 使用Mapper代理对象进行数据库操作。通过调用Mapper接口的方法,可以实现对数据库的增删改查操作。 整个过程中,Spring Boot通过自动配置和注解扫描的方式,简化了Spring整合Mybatis的配置和使用过程,使得开发者可以更方便地使用Mybatis进行数据库操作。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [SpringBoot整合Mybatis源码解析](https://blog.csdn.net/u013521882/article/details/120624374)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Spring-Mybatis整合源码分析](https://blog.csdn.net/qq_42651904/article/details/111059652)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Spring源码解析之整合Mybatis](https://blog.csdn.net/heroqiang/article/details/79135500)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值