springboot整合mybatis以及mybatis源码分析

1、@ComponentScan basePackages与value: 用于指定包的路径,进行扫描 basePackageClasses: 用于指定某个类的包的路径进行扫描 nameGenerator: bean的名称的生成器 useDefaultFilters: 是否开启对@Component,@Repository,@Service,@Controller的类进行检测 url: https://blog.csdn.net/u012326462/article/details/82765485

2、@ImportResource注解 用于导入 Spring 的 xml 配置文件,让该配置文件中定义的 bean 对象加载到Spring容器中。 参考链接:https://blog.csdn.net/lzb348110175/article/details/105148214

3、 引入依赖

<dependency> <groupId>org.mybatis.spring.boot</groupId>

<artifactId>mybatis-spring-boot-starter</artifactId> </dependency>

在上述BeanFactory的后置处理器中就会读取mybatis-spring-boot-autoconfigure-***.jar包中的spring.factories文件 /org/mybatis/spring/boot/mybatis-spring-boot-autoconfigure/2.1.0/mybatis-spring-boot-autoconfigure-2.1.0.jar!/META-INF/spring.factories org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

<!-- 通过扫描的模式,扫描目录在mapper目录下,所有的mapper都继承SqlMapper接口的接口, 这样一个bean就可以了-->

@MapperScan和@Mapper都可以,@Mapper每个接口都要写 为什么没有加@MapperScan注解也可以扫描mapper接口 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.hsjry.convert.dal.dao.mapper.**" /> <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" /> </bean>

扫描调用栈 1.org.mybatis.spring.annotation.MapperScannerRegistrar#registerBeanDefinitions(org.springframework.core.annotation.AnnotationAttributes, org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.String)

2.org.mybatis.spring.annotation.MapperScannerRegistrar#registerBeanDefinitions(org.springframework.core.annotation.AnnotationAttributes, org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.String) 声明要注册的对象 BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);

3. 向这个注册中心注册一个新的 bean 定义 要用到BeanDefinitionRegistry org.springframework.beans.factory.support.BeanDefinitionRegistry#registerBeanDefinition

4.org.mybatis.spring.mapper.MapperScannerConfigurer#postProcessBeanDefinitionRegistry

5.org.springframework.context.annotation.ClassPathBeanDefinitionScanner#scan

6.org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan @return 注册的 bean set集 Set<BeanDefinitionHolder>

7.org.mybatis.spring.mapper.ClassPathMapperScanner#doScan

8.ClassPathMapperScanner extends ClassPathBeanDefinitionScanner

9.扫描到mapper接口了 org.mybatis.spring.mapper.ClassPathMapperScanner#processBeanDefinitions

10.ClassPathMapperScanner#processBeanDefinitions // 处理BeanDefinitions // 入参的beanDefinitions是从mapper接口产生的 private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) { GenericBeanDefinition definition; for (BeanDefinitionHolder holder : beanDefinitions) { definition = (GenericBeanDefinition) holder.getBeanDefinition(); // the mapper interface is the original class of the bean // but, the actual class of the bean is MapperFactoryBean // 是为了创建mapperFactoryBean对象时,给mapperInterface赋值 definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // 设置beanClass = mapperFactoryBean.class definition.setBeanClass(this.mapperFactoryBean.getClass()); ....... // 按类型注入,作用是在创建MapperFactoryBean对象时,通过set方法按类型实现装配 definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE); } } }

11.org.mybatis.spring.mapper.MapperFactoryBean#getObject public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> 很多都是在spring做扩展

12.org.apache.ibatis.session.SqlSession#getMapper interface

13.org.mybatis.spring.SqlSessionTemplate#getMapper

14.org.apache.ibatis.session.Configuration#getMapper 这个就是SqlSession接口的方法

15.org.apache.ibatis.binding.MapperRegistry#getMapper

16.org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.session.SqlSession)

17.org.apache.ibatis.binding.MapperProxyFactory#newInstance(org.apache.ibatis.binding.MapperProxy<T>),在这个方法中,使用JDK动态代理,创建代理对象,该代理对象就是我们Service层的代码中注入的对象 dao层接口。

二.MyBatis的SqlSession是非线程安全的,那Spring的SqlSessionTemplate是怎么解决的这个问题?

1.org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke

2.org.mybatis.spring.SqlSessionUtils#getSqlSession(org.apache.ibatis.session.SqlSessionFactory, org.apache.ibatis.session.ExecutorType, org.springframework.dao.support.PersistenceExceptionTranslator):

public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {

notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED); notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);

//从事务同步管理器中取sqlSession

SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);

SqlSession session = sessionHolder(executorType, holder);

//有就返回 if (session != null) { return session; } LOGGER.debug(() -> "Creating a new SqlSession");

//没有就创建 session = sessionFactory.openSession(executorType);

//放到事务同步管理器中 registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session); return session; }

MyBatis 方法调用路由到从 Spring 的事务管理器获得的正确 SqlSession 事务同步管理器可以认为是一个ThreadLocal的map,这样同一个线程在使用SqlSessionTemplate多次对数据库操作的时候,取到的是同一个SqlSession;而不同的线程取到的是不同的SqlSession,所以说SqlSessionTemplate是线程安全的。

三 MyBatis怎么跟Spring声明式事务配合完成的数据库事务内的增删改查操作?

1.设置数据库连接的自动提交为false

2.通过这个数据库连接进行sql语句执行

3.异常回滚或者提交 以上三步中的1和3是Spring声明式事务做的,2是MyBatis做的,它们是怎么配合的呢?其实只需要保证1、2、3这三步中获取到的是同一个数据库连接就好,其实也是通过事务同部管理器这个ThreadLocal的map来实现的 1.org.springframework.transaction.interceptor.TransactionInterceptor#invoke>>

TransactionInterceptor extends TransactionAspectSupport

//存在在threadLocal里面去 

org.springframework.transaction.interceptor.TransactionAspectSupport#invokeWithinTransaction 》》

org.springframework.transaction.interceptor.TransactionAspectSupport.TransactionInfo#bindToThread

2.org.mybatis.spring.SqlSessionUtils#getSqlSession(org.apache.ibatis.session.SqlSessionFactory, org.apache.ibatis.session.ExecutorType, org.springframework.dao.support.PersistenceExceptionTranslator)

去spring 管理的事务同步管理器中拿sqlSession 拿不到就拿传进来的SqlSessionFactory去开一个session org.apache.ibatis.session.SqlSessionFactory#openSession(org.apache.ibatis.session.ExecutorType) >>> org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSession(org.apache.ibatis.session.ExecutorType)>>>

org.apache.ibatis.session.defaults.DefaultSqlSessionFactory#openSessionFromDataSource>>>

private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {

Transaction tx = null; try { final Environment environment = configuration.getEnvironment();

//创建的是 就是TransactionFactory实现类org.apache.ibatis.transaction.managed.ManagedTransactionFactory

//从环境配置中拿到的是org.apache.ibatis.transaction.TransactionFactory final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);

// autoCommit是false

//new org.mybatis.spring.transaction.SpringManagedTransaction tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);

final Executor executor = configuration.newExecutor(tx, execType);

return new DefaultSqlSession(configuration, executor, autoCommit);

}

catch (Exception e) { closeTransaction(tx);

// may have fetched a connection so lets call close() throw ExceptionFactory.wrapException("Error opening session. Cause: " + e, e);

}

finally {

ErrorContext.instance().reset();

}

}

3.org.mybatis.spring.transaction.SpringManagedTransaction#getConnection>>

org.mybatis.spring.transaction.SpringManagedTransaction#openConnection>>

org.springframework.jdbc.datasource.DataSourceUtils#getConnection>>

org.springframework.jdbc.datasource.DataSourceUtils#doGetConnection

四.springboot整合mybatis是怎么做到不用配置mybatis-config.xml的呢?

1.https://mybatis.org/mybatis-3/configuration.html mybatis-config.xml属性参考官网文档

2.用mybatis-config.xml配置文件 org.apache.ibatis.session.SqlSessionFactoryBuilder#build(java.io.Reader, java.lang.String, java.util.Properties)》》

//解析配置文件得到Configuration对象,得到DefaultSqlSessionFactory对象

org.apache.ibatis.session.SqlSessionFactoryBuilder#build(org.apache.ibatis.session.Configuration)》》

org.apache.ibatis.builder.xml.XMLConfigBuilder#parse》》

org.apache.ibatis.builder.xml.XMLConfigBuilder#parseConfiguration 这里的顺序有依赖关系 properties先解析 后面environments,databaseIdProvider都可以用到

properties》settings》typeAliases》plugins》objectFactory》objectWrapperFactory》reflectorFactory》environments》databaseIdProvider》typeHandlers》mappers

3.springboot自动配置 引入依赖 <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> </dependency> 要使配置生效两种方式

1、@EnableConfigurationProperties(MybatisProperties.class)org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration @EnableConfigurationProperties 注解的作用是:让使用了 @ConfigurationProperties 注解的类生效,并且将该类注入到 IOC 容器中,交由 IOC 容器进行管理

2、使用 @ConfigurationProperties + @Component 注解 用于自定义默认设置的配置对象。如果指定了 {@link #configLocation},则 * 不使用此属性

@NestedConfigurationProperty private Configuration configuration; 表示 {@link ConfigurationProperties} 对象中的字段应该被视为 * 如果它是嵌套类型。此注释与实际绑定 * 进程无关,但它被 {@code spring-boot-configuration-processor} 用作提示 * 字段未绑定为单个值。指定时,*为该字段创建一个嵌套组,并获取其类型。 * <p> * 这对collections and maps没有影响,因为这些类型是自动识别的 前缀是mybatis加@NestedConfigurationProperty 我们就可以表明它是嵌套组了 可以在配置设置这个对象里面的值

mybatis:

  configuration:

    cache-enabled:

@AutoConfigureAfter({ DataSourceAutoConfiguration.class, MybatisLanguageDriverAutoConfiguration.class })

public class MybatisAutoConfiguration implements InitializingBean {

提示应该在其他指定的自动配置类之后应用 {@link EnableAutoConfiguration 自动配置} 这个注解表示DataSourceAutoConfiguration MybatisLanguageDriverAutoConfiguration要在MybatisAutoConfiguration 后执行 org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration》》

org.springframework.boot.autoconfigure.jdbc.DataSourceProperties 前缀是spring.datasource 动态代理

 

spring整合mybatis的debug的步骤:

org.apache.ibatis.session.defaults.DefaultSqlSession@14d6f58f

org.apache.ibatis.session.SqlSession#insert(java.lang.String, java.lang.Object)

org.apache.ibatis.binding.MapperProxy#invoke》》

org.apache.ibatis.binding.MapperMethod#execute》》

org.mybatis.spring.SqlSessionTemplate#insert(java.lang.String, java.lang.Object)》》

org.mybatis.spring.SqlSessionTemplate.SqlSessionInterceptor#invoke》》

org.apache.ibatis.session.defaults.DefaultSqlSession#insert(java.lang.String, java.lang.Object)》》也是通过代理生成的,传统的操作是直接工厂模式new

org.apache.ibatis.session.defaults.DefaultSqlSession#update(java.lang.String, java.lang.Object)》》

org.apache.ibatis.plugin.Plugin#invoke》》

org.apache.ibatis.executor.CachingExecutor#update》》这里面不用创建缓存只有query才需要

org.apache.ibatis.executor.BaseExecutor#update》》

org.apache.ibatis.executor.BaseExecutor#doUpdate》》

org.apache.ibatis.executor.SimpleExecutor#doUpdate》》

org.apache.ibatis.executor.statement.SimpleStatementHandler#update》》

java.sql.Statement#execute(java.lang.String)

jdbc里内容了

@Override
public int doUpdate(MappedStatement ms, Object parameter) throws SQLException {
  Statement stmt = null;
  try {
    Configuration configuration = ms.getConfiguration();
    StatementHandler handler = configuration.newStatementHandler(this, ms, parameter, RowBounds.DEFAULT, null, null);
    stmt = prepareStatement(handler, ms.getStatementLog());
    return handler.update(stmt);
  } finally {
    closeStatement(stmt);
  }
}

 

 Plugin里的invoke方法

 

 

一堆代理

 

 

posted @ 2022-08-23 17:27  宁山  阅读(373)  评论(0编辑  收藏  举报