mybatis-spring:SqlSessionFactoryBean

引言

demo: springboot+mybatis的MybatisConfig中有这样两个bean配置(事务此时不看):

    // 数据源配置
    @Bean
    public DataSource dataSource() {
        // mybatis自带的一个简易数据库连接池,只是为了debug代码,这个就不关心了
        PooledDataSource pooledDataSource = new PooledDataSource();
        pooledDataSource.setDriver(environment.getProperty("mysql.driver"));
        pooledDataSource.setUsername(environment.getProperty("mysql.username"));
        pooledDataSource.setPassword(environment.getProperty("mysql.passwd"));
        pooledDataSource.setUrl(environment.getProperty("mysql.url"));
        return pooledDataSource;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws IOException {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        ResourcePatternResolver resourcePatternResolver = new PathMatchingResourcePatternResolver();
        sqlSessionFactoryBean.setMapperLocations(resourcePatternResolver.getResources("classpath:/mapper/*.xml"));
        return sqlSessionFactoryBean;
    }

dataSource数据源配置的bean,是通过依赖注入传递给SqlSessionFactoryBean这个bean了。

问题点在于SqlSessionFactoryBean在整个容器初始化过程中起了什么作用。

@MappserScan

mybatis-spring:@MapperScan注解中解释使用这个注解,完成两件事:

                  1. 扫描指定接口

                  2. 注册这些接口的bean定义到spring容器(实际是FactoryBean定义)

然后spring容器在对Dao层接口注入的时候,实际注入的是一个Mybatis创建的代理对象:MapperProxy,

public class MapperProxy<T> implements InvocationHandler, Serializable {

  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }
}

看构造方法和属性字段,mapperInterface是被代理的接口,methodCache是接口的方法,另外一个关键属性是sqlSession。

SqlSession是MyBatis 的一个主要 Java 接口。通过它的实现可以执行SQL命令、执行事务等。所以它是执行SQL的关键。现在需要确认的是SqlSession与SqlSessionFactoryBean的关系。

SqlSessionFactoryBean

见名知义,SqlSessionFactoryBean是创建SqlSessionFactory的:

public class SqlSessionFactoryBean implements FactoryBean<SqlSessionFactory>, InitializingBean, ApplicationListener<ApplicationEvent> {}

它也实现了FactoryBean,并且实现了InitializingBean接口的afterPropertiesSet()方法。这样在spring容器实例化这个bean的时候,便会回调afterPropertiesSet()方法:

  @Override
  public void afterPropertiesSet() throws Exception {
    notNull(dataSource, "Property 'dataSource' is required");
    notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");

    this.sqlSessionFactory = buildSqlSessionFactory();
  }

来创建SqlSessionFactory实例,然后看下它对FactoryBean的接口方法实现:

  @Override
  public SqlSessionFactory getObject() throws Exception {
    if (this.sqlSessionFactory == null) {
      afterPropertiesSet();
    }

    return this.sqlSessionFactory;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public Class<? extends SqlSessionFactory> getObjectType() {
    return this.sqlSessionFactory == null ? SqlSessionFactory.class : this.sqlSessionFactory.getClass();
  }

spring容器中某个bean的字段需要依赖注入SqlSessionFactory实例的时候,会调用getObject方法,返回SqlSessionFactory实例。

那么哪个bean的属性需要注入SqlSessionFactory?答案是:MapperFactoryBean。

上文说扫描到的每个接口注册的工厂bean定义的就是MapperFactoryBean这个bean定义。

它的用处就是注入service的dao层接口属性(如在ServiceImpl的属性UserDao)的代理对象的时候,会返回这个MapperProxy代理对象。mybatis-spring:@MapperScan注解文中有提到解释。

问题来了,这里注入的是SqlSessionFactory对象,MapperProxy需要的是SqlSession实现类。答案看下面:

MapperFactoryBean继承自SqlSessionDaoSupport:

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {}

在父类SqlSessionDaoSupport里有方法:

  public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
    if (!this.externalSqlSession) {
      this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
    }
  }

而SqlSessionTemplate是SqlSession的实现:

public class SqlSessionTemplate implements SqlSession, DisposableBean {

  private final SqlSessionFactory sqlSessionFactory;

  private final ExecutorType executorType;

  private final SqlSession sqlSessionProxy;

  private final PersistenceExceptionTranslator exceptionTranslator;
}

而它的sqlSessionProxy实例是从它的构造方法里构造的:

  public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
      PersistenceExceptionTranslator exceptionTranslator) {

    notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
    notNull(executorType, "Property 'executorType' is required");

    this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }

所以MapperProxy的SqlSession实例,实际是SqlSessionTemplate实例,下层再委托给sqlSessionProxy代理对象进行实际调用。

因此,注入ServiceImpl的UserDao属性字段时,发生以下过程:

1. spring容器注入UserDao属性的MapperProxy实例时,依赖MapperProxy实例

2. 此时实例MapperFactoryBean来创建MapperProxy实例

3. 创建MapperFactoryBean需要依赖注入SqlSessionFacotry实例

4. 使用SqlSessionFactoryBean来创建SqlSessionFactory实例

最终完成整个过程,注入userDao属性的值 MapperProxy实例。

发布了136 篇原创文章 · 获赞 81 · 访问量 18万+

猜你喜欢

转载自blog.csdn.net/x763795151/article/details/100027600