2020-11-11 08:52

spring整合mybatis底层代码分析

王姐姐

JavaEE

(1520)

(0)

收藏

mybatis没有与Spring整合前是这样使用的

@Test
public void test03(){
   String resource = "mybatis-config.xml";
   InputStream inputStream = Resources.getResourceAsStream(resource);
   //1.获取sqlSessionFactory
   SqlSessionFactory sqlSessionFactory= new SqlSessionFactoryBuilder().build(inputStream);
   //2.获取SqlSession 
   SqlSession session = sqlSessionFactory.openSession();
   //3.获取mapper
   TblEmployeeMapper mapper = session.getMapper(TblEmployeeMapper.class);
   TblEmployeePO tblEmployeePO=new TblEmployeePO();
   tblEmployeePO.setId(null);
   tblEmployeePO.setLastName("王五");
   tblEmployeePO.setGender("1");
   tblEmployeePO.setEmail("a");
   //4.调用增删改查方法
   mapper.insert(tblEmployeePO);
   session.commit();
   session.close();}1234567891011121314151617181920

可以看到要执行增删改查之前必须获得,sqlSessionFactory,SqlSession 和对应的mapper。

Spring整合mybatis

整合部分的代码:


可以看到通过标签往容器中注入的是sqlSessionFactoryBean,注入的并不是sqlSessionFactory,或者SqlSession,看看这个sqlSessionFactoryBean是什么。

SqlSessionFactoryBean

class SqlSessionFactoryBean implements FactoryBean

可以看到它实现了FactoryBean
1.实现FactoryBean有什么用?
FactoryBean接口:实现了该接口的类,在调getBean的时候会返回该工厂返回的实例对象,也就是再调一次getObject方法返回工厂的实例。也就是说SqlSessionFactoryBean 的getObject方法能返回SqlSessionFactory的实例对象。

2.实现InitializingBean有什么用?
InitializingBean接口:实现了这个接口,那么当bean初始化的时候,spring就会调用该接口的实现类的afterPropertiesSet方法,去实现当spring初始化该Bean 的时候所需要的逻辑。

SqlSessionFactoryBean的初始化

public void afterPropertiesSet() throws Exception {
    Assert.notNull(this.dataSource, "Property 'dataSource' is required");
    Assert.notNull(this.sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
    Assert.state(this.configuration == null && this.configLocation == null || this.configuration == null || this.configLocation == null, "Property 'configuration' and 'configLocation' can not specified with together");
    //创建一个sqlSessionFactory,注意这个sqlSessionFactory在这里并没有被注入到springIOC容器中
    this.sqlSessionFactory = this.buildSqlSessionFactory();}1234567

从中我们可以看到,sqlSessionFactory的实例化便在这个方法里面实例化,buildSqlSessionFactory()方法会对我们的sqlSessionFactory做定制的初始化,初始化sqlSessionFactory有两种方式,一种是我们直接通过property直接注入到该实例中,另一种是通过解析xml的方式,就是我们在全局配置文件里面的配置,根据这些配置做了相应的初始化操作,里面也是一些标签的解析属性的获取,操作,和Spring的默认标签解析有点类似,这里就不再重复说明。

获取SqlSessionFactoryBean实例
因为SqlSessionFactoryBean实现了FactoryBean接口,所以当我们通过getBean获取它的实例的时候实际是调用他的getObject方法,获取到的是sqlSessionFactory。

//返回一个sqlSessionFactory对象,这个对象会添加到容器中,这里才把sqlSessionFactory给添加到容器中
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }
    return this.sqlSessionFactory;
  }1234567

MapperFactoryBean

在使用mybatis的时候,我们获取dao的方式一般是这样

SqlSession openSession = sqlSessionFactory.openSession();DepartmentMapper mapper = openSession.getMapper(DepartmentMapper.class);12

但在我们在spring的测试用例中使用mybatis的时候是这样使用的:

TblEmployeeMapper bean = ioc.getBean(TblEmployeeMapper.class);1

也就是说xxxMapper已经是在ioc容器中的了。


MapperFactoryBean:根据指定的Mapper接口生成Bean实例

public class MapperFactoryBean

获取MapperFactoryBean实例

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

看到这里,我们会恍然大悟,原来在这里封装了getMapper操作,返回接口的实例,怪不得在Spring中使用MyBatis我们不用管理sqlSession了。


问题1:那么sqlSession是获取的呢?
SqlSessionDaoSupport是MapperFactoryBean父类

SqlSessionDaoSupport类看到:
public SqlSession getSqlSession() {
    return this.sqlSessionTemplate;}发现其实获取到的SqlSession是sqlSessionTemplate类型的
SqlSessionDaoSupport类看到:
public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (this.sqlSessionTemplate == null || sqlSessionFactory != this.sqlSessionTemplate.getSqlSessionFactory()) {
        this.sqlSessionTemplate = this.createSqlSessionTemplate(sqlSessionFactory);
    }}证实了
protected SqlSessionTemplate createSqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    return new SqlSessionTemplate(sqlSessionFactory);}123456789101112131415

问题2:之前SqlSessionFactoryBean被注入到了容器中,MapperFactoryBean是如何获取到SqlSessionFactory ,然后利用SqlSessionFactory 创建sqlSession的呢?

1.mapperInterface:用于指定接口2.sqlSessionFactory:从容器中获取sqlSessionFactory并赋值到属性sqlSessionFactory中3.sqlSessionTemplate:用于指定SqlSessionTemplate。如果和sqlSessionFactory同时配置,则只会启用sqlSessionTemplate。1234567

这里通过,把sqlSessionFactory给注入到了MapperFactoryBean中,然后MapperFactoryBean就能利用sqlSessionFactory创建SqlSession了。

MapperFactoryBean初始化

MapperFactoryBean继承了SqlSessionDaoSupport,SqlSessionDaoSupport继承DaoSupport,DaoSupport实现了InitializingBean接口,让我们开看看它这接口的实现:

public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
	// Let abstract subclasses check their configuration.
	checkDaoConfig();

	// Let concrete implementations initialize themselves.
	try {
		initDao();
	}
	catch (Exception ex) {
		throw new BeanInitializationException("Initialization of DAO failed", ex);
	}}123456789101112

该方法主要包含两个功能,一个是调用checkDaoConfig()方法,一个是调用initDao方法。checkDaoConfig方法在DaoSupport是抽象方法,让我看看它在MapperFactoryBean的实现:

@Override
protected void checkDaoConfig() {
  super.checkDaoConfig();

  Configuration configuration = getSqlSession().getConfiguration();
  if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
    try {
      configuration.addMapper(this.mapperInterface);
    } catch (Throwable t) {
      logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", t);
      throw new IllegalArgumentException(t);
    } finally {
      ErrorContext.instance().reset();
    }
  }}12345678910111213141516

该方法主要是检查dao的配置,主要是检验sqlSessionFactory和mapperInterface属性不能为空,以及检测接口对于的映射文件是否存在,如果存在,那么就把它添加到configuration里面去,注册mapper。这个configuration是SqlSessionFactory实现类DefaultSqlSessionFactory的属性。

public T getObject() throws Exception {
   return this.getSqlSession().getMapper(this.mapperInterface);}SqlSessionTemplate类:这里的configuration就是DefaultSqlSessionFactory中的
public

注:我们一般不直接使用MapperFactoryBean,如果在配置文件中直接使用MapperFactoryBean(具体配置看问题2),那么项目中那么多的xxxMapper,不太现实,使用MapperScannerConfigurer可以解决这个问题。

MapperScannerConfigurer

如果我们的dao在一个包下面又好几十个,那么我可以可以通过扫描的方式添加dao,像下面一样使用

 123456789101112

看到上面的配置,我们会很好奇,在spring这样添加就可以扫描的方式添加dao配置,怎么做到的?让我打开类实现,具体看一下
MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,如果MapperScannerConfigurer实现了该接口,那么说明在application初始化的时候该接口会被调用,具体实现,让我先看看:

  /**
   * {@inheritDoc}
   * 
   * @since 1.0.2
   */
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
    if (this.processPropertyPlaceHolders) {
    //执行属性的处理,简单的说,就是把xml中${XXX}中的XXX替换成属性文件中的相应的值
      processPropertyPlaceHolders();
    }
 
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
    scanner.setAddToConfig(this.addToConfig);
    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.registerFilters();//该方法主要做了以下操作://1)扫描basePackage下面的java文件//2)解析扫描到的java文件//3)调用各个在上一步骤注册的过滤器,执行相应的方法。//4)为解析后的java注册bean,注册方式采用编码的动态注册实现。//5)构造MapperFactoryBean的属性,mapperInterface,sqlSessionFactory等等,填充到BeanDefinition里面去。
    scanner.scan(StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }12345678910111213141516171819202122232425262728293031

做完这些,MapperFactoryBean对象也就构造完成了,扫描方式添加dao的工作也完成了。

附:

SqlSessionTemplate:

https://zhidao.baidu.com/question/2016521463125168748.html1

关于Mybatis与Spring整合之后SqlSession与mapper对象之间数量的问题。

https://www.cnblogs.com/ljdblog/p/7123430.html1

SqlSessionFactoryBean和MapperFactoryBean作用:

https://www.jianshu.com/p/3e39a3bf7ccb1

spring 整合 mybatis原理

https://blog.csdn.net/qq_43193797/article/details/850116831

Mybatis之工作原理

https://blog.csdn.net/u014297148/article/details/786960961

SqlSessionFactoryBean
-----为整合应用提供SqlSession对象资源,取得SqlSessionFactoryBean实例

123456

MapperFactoryBean
------根据指定的Mapper接口生成Bean实例
MapperFactoryBean创建的代理类实现了UserMapper接口,并且注入到应用程序中。 因为代理创建在运行时环境中(Runtime,译者注) ,那么指定的映射器必须是一个接口,而不是一个具体的实现类。
上面的配置有一个很大的缺点,就是系统有很多的配置文件时全部需要手动编写,太麻烦了。

1.mapperInterface:用于指定接口2.sqlSessionFactory:从容器中获取sqlSessionFactory并赋值到属性sqlSessionFactory中3.sqlSessionTemplate:用于指定SqlSessionTemplate。如果和sqlSessionFactory同时配置,则只会启用sqlSessionTemplate。1234567

MapperScannerConfigurer
它 将 会 查 找 类 路 径 下 的 映 射 器 并 自 动 将 它 们 创 建 成 MapperFactoryBean。
老式写法:

1234

新写法:新写法的注解形式@MapperScan("org.mybatis.spring.sample.mapper")


转自:https://blog.csdn.net/weixin_42412601/article/details/104600907

0条评论

点击登录参与评论