SqlSessionTemplate使用SqlSessionFactoryBean实例化之谜(以及xml配置信息迁移到java的方法)

最近在学习mybatis源码时,发现了一个问题。

首先,在xml配置文件中配置了SqlSessionTemplate与SqlSessionFactoryBean。

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
  <property name="configLocation" value="classpath:${mybatis.config}"/>
  <property name="mapperLocations" value="classpath*:mybatis/mapper.xml"/>
</bean>
<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
  <constructor-arg>
    <ref bean="sqlSessionFactory" />
  </constructor-arg>
</bean>

看起来,SqlSessionTemplate是使用SqlSessionFactoryBean实例化的。

然而,查看SqlSessionTemplate的构造方法,发现:

public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
  this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
}

SqlSessionTemplate其实是用SqlSessionFactory实例化的!

可是xml中明明写着这个bean的类型是SqlSessionFactoryBean呀,难道SqlSessionFactoryBean继承或实现了SqlSessionFactory?

继续查看源码,发现:

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

总之,SqlSessionFactoryBean并没有继承或实现了SqlSessionFactory。

这样就奇怪了,类型不匹配的话,SqlSessionTemplate到底是怎么实例化的?

后来,通过百度发现:

SqlSessionFactoryBean 实现了 Spring 的 FactoryBean 接口,
就说明了由 Spring 最终创建的 bean 不是 SqlSessionFactoryBean 本身,
而是工厂类的 getObject() 返回的方法的结果。
这种情况下, Spring 将会在应用启动时为你创建 SqlSessionFactory 对象,然后将它以  SqlSessionFactory 为名来存储。

好吧,这波操作666,本人完全没想到。

本人猜测,可能是这样实现的(正好举一个xml迁移进java的例子):

import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import javax.sql.DataSource;

@Configuration
public class MybatisConfig {
    //从spring容器中获取配置数据源的其它bean对象
    @Resource
    private DataSource dataSource;
    //从spring容器中获取处理好的对象,对应:
    //classpath:${mybatis.config}
    @Resource
    private org.springframework.core.io.Resource configLocation;
    //从spring容器中获取处理好的对象,对应:
    //classpath*:mybatis/mapper.xml
    @Resource
    private org.springframework.core.io.Resource[] mapperLocations;

    @Bean(name="sqlSessionFactory")
    public SqlSessionFactory sqlSessionFactoryBeanTosqlSessionFactory() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        sqlSessionFactoryBean.setConfigLocation(configLocation);
        sqlSessionFactoryBean.setMapperLocations(mapperLocations);
        SqlSessionFactory sqlSessionFactory = sqlSessionFactoryBean.getObject();
        //虽然new了SqlSessionFactoryBean,但是返回的是SqlSessionFactory
        return sqlSessionFactory;
    }
}

你以为xml中配了class,获得的对象就一定是class指定的对象吗?

你以为是SqlSessionFactoryBean,其实是我SqlSessionFactory哒!

不得不说,源码这波操作是真的6……

posted @ 2020-10-22 16:44  codeToSuccess  阅读(421)  评论(0编辑  收藏  举报