Author 罗先生
本文是IOC系列第四篇。往期回顾:

FactoryBean是什么?

首先看懂这篇,需要先看SpringIOC-FactoryBean

SqlsessionFactoryBean

类声明:

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

这里我们需要的是他和SessionFactory没有继承实现关系,只是FactoryBean声明的泛型是SqlSessionFactory不声明关系也不大。。都是为了严谨)

没有SqlsessionFactoryBean的Mybatis

举个例子,打开mybatis官网(https://mybatis.org/mybatis-3/getting-started.html),getstart会教你怎么搭建Mybatis环境,然后就会看到一大堆XMl配置,

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${driver}"/>
        <property name="url" value="${url}"/>
        <property name="username" value="${username}"/>
        <property name="password" value="${password}"/>
      </dataSource>
    </environment>
  </environments>
  <mappers>
    <mapper resource="org/mybatis/example/BlogMapper.xml"/>
  </mappers>
</configuration>

之后就是Building SqlSessionFactory这个过程,最后会执行build方法

DataSource dataSource = BlogDataSourceFactory.getBlogDataSource();
TransactionFactory transactionFactory =
  new JdbcTransactionFactory();
Environment environment =
  new Environment("development", transactionFactory, dataSource);
Configuration configuration = new Configuration(environment);
configuration.addMapper(BlogMapper.class);
SqlSessionFactory sqlSessionFactory =
  new SqlSessionFactoryBuilder().build(configuration);

可以发现build出来的是DefaultSqlSessionFactory对象,他是SqlSessionFactory的一个实现类,而上面的xml配置最终是为了配置Configuration对象,因为DefaultSqlSessionFactory的实例化需要它

public SqlSessionFactory build(Configuration config) {
    return new DefaultSqlSessionFactory(config);
}

既然都贴了这么多文档上的东西,就全部贴完吧,其实下面就是应用了,上面已经把环境配置好了

try (SqlSession session = sqlSessionFactory.openSession()) {
  BlogMapper mapper = session.getMapper(BlogMapper.class);
  Blog blog = mapper.selectBlog(101);
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
  PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.mybatis.example.BlogMapper">
  <select id="selectBlog" resultType="Blog">
    select * from Blog where id = #{id}
  </select>
</mapper>

缺点:

SqlSessionFactory 的构造需要Configuration,而Configuration 需要依赖Environment ,而Environment 依赖比较多,他的构造要一个字符串,dataSource,还有一个TransactionFactory 对象,然后设置好Configuration后,还需要手动add mapper,整个流程下来稍微复杂,而如果Mybatis以后想新增别的依赖,这里的Java代码配置更是复杂度上升,所以Mybatis在整合Spring的时候,就使用了FactoyBean的方式来对外提供一种简单的关系。

有了SqlsessionFactoryBean之后

@Configuration
public class Appconfig {
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }
    @Bean
    public DataSource dataSource() {
        DriverManagerDataSource driverManagerDataSource = new DriverManagerDataSource();
        driverManagerDataSource.setPassword("root");
        driverManagerDataSource.setUsername("root");
        driverManagerDataSource.setUsername("jdbc:mysql://localhost:3306/test");
        driverManagerDataSource.setDriverClassName("com.mysql.jdbc.Driver");
        return driverManagerDataSource;
    }
}

优点:

简单了太多,可以发现只需要一个数据源 + SqlSessionFactoryBean ,

SqlSessionFactoryBean帮我们做了什么

我们在IOC应用-SqlsessionFactoryBean 说到了FactoryBean最终会往容器里注册一个getObject()返回的对象,如下:

@Override
public SqlSessionFactory getObject() throws Exception {
  if (this.sqlSessionFactory == null) {
    afterPropertiesSet();
  }
  return this.sqlSessionFactory;
}
public void afterPropertiesSet() throws Exception {
  notNull(dataSource, "Property 'dataSource' is required");
  notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
  state((configuration == null && configLocation == null) || !(configuration != null && configLocation != null),
      "Property 'configuration' and 'configLocation' can not specified with together");

  this.sqlSessionFactory = buildSqlSessionFactory();
}
protected SqlSessionFactory buildSqlSessionFactory() throws Exception {
... //上面都不用看
targetConfiguration.setEnvironment(new Environment(this.environment,
    this.transactionFactory == null ? new SpringManagedTransactionFactory() : this.transactionFactory,
    this.dataSource));
    return this.sqlSessionFactoryBuilder.build(targetConfiguration);
}

看到**buildSqlSessionFactory的setEnvironment()**就比较熟悉了。上面的代码还是比较容易看懂的

总结:

SqlSessionFactoryBean其实就是把内部创建SqlSessionFactory的复杂依赖关系都自己维护了,除了需要外部指定的dataSource,所以再把dataSource set进SqlSessionFactoryBean,剩下的SqlSessionFactoryBean会自己解决,最后在getObject()方法中返回的就是一个初始化好的SqlSessionFactory 对象到容器当中。