MyBatis-Spring核心源码解析之SqlSessionFactoryBean+BasicDataSource

本文围绕MyBatis和Spring展开,分析了SqlSessionFactoryBean实现的FactoryBean、InitializingBean、ApplicationListener三个Spring接口,介绍其初始化源码及注入SqlSessionFactory的过程。还阐述了BasicDataSource创建连接池和获取数据库连接的详细步骤,展示了如何简化数据库操作配置。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言


在之前使用了mybatis的文档中,我们需要手动创建SqlSessionFactory对象来获取SqlSession,然后通过SqlSession获取mapper代理对象,进行数据库操作。

  // 根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象
    String resource = "mybatis-config.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    // 通过SqlSessionFactory获取SqlSession实例
    SqlSession sqlSession = sqlSessionFactory.openSession();
    UserMapper mapper = sqlSession.getMapper(UserMapper.class);

SqlSessionFactoryBean 从名字就能看出来它是用来创建工厂类的,继承关系如下:

将SqlSessionFactory和Mapper代理对象交给了spring去创建和管理,其中重要的一步是注入了SqlSessionFactory ,代码如下:

    

@Bean
    public SqlSessionFactory sqlSessionFactory() throws Exception {
        // 创建SqlSessionFactoryBean对象
        SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
        // 设置数据源
        factoryBean.setDataSource(dataSource());
        // SqlSessionFactoryBean 对象获取SqlSessionFactory,并装载到IOC中
        return factoryBean.getObject();
    }



接下里我们分析下SqlSessionFactoryBean。

SqlSessionFactoryBean实现的接口有哪些


SqlSessionFactoryBean实现了三个重要的Spring接口:

  1. FactoryBean< SqlSessionFactory>,
  2. InitializingBean,
  3. ApplicationListener< ApplicationEvent>


1. FactoryBean


SqlSessionFactoryBean 实现了 Spring 的 FactoryBean 接口。 这意味着由 Spring 最终创建的 bean 并不是 SqlSessionFactoryBean 本身,而是工厂类(SqlSessionFactoryBean)的 getObject() 方法的返回结果。

这种情况下,Spring 将会在应用启动时为你创建 SqlSessionFactory,并使用 sqlSessionFactory 这个名字存储起来。

FactoryBean定义了三个方法,其源码如下:

public interface FactoryBean<T> {
    String OBJECT_TYPE_ATTRIBUTE = "factoryBeanObjectType";
    // 获取泛型T的实例。用来创建Bean。当IoC容器通过getBean方法来创建FactoryBean的实例时实际获取的不是FactoryBean 本身,而是具体创建的T泛型实例。
    @Nullable
    T getObject() throws Exception;
    // 返回FactoryBean创建的bean类型。
    @Nullable
    Class<?> getObjectType();
    // 返回由FactoryBean创建的bean实例的作用域是singleton还是prototype。
    default boolean isSingleton() {
        return true;
    }
}


在使用Spring声明一个Bean后,比如下面声明了一个名为animal的Bean,那么Spring通过反射机制利用bean的class属性指定实现类来实例化bean ,然后存放在IOC中。

    <!--注入属性时还可以使用 p 名称空间注入,可以简化基于 xml 配置方式-->
    <bean id="animal" class="org.pearl.spring.demo.pojo.Animal" p:age="18" p:name="使用P注入"/>


如果我们相对这个Bean进行属性配置,或者需要增强某些功能,采用以上的方式就比较麻烦了,这个时候我们可以声明当前类为FactoryBean的泛型,对这个Bean对象进行属性设置功能增强,再在getObject方法中获取这个Bean注入到IOC中。

2. InitializingBean


InitializingBean从字面上理解是初始化Bean,该接口在容器为 bean 设置所有必要的属性后,让 bean 执行初始化工作。该InitializingBean接口指定了一个方法:

void afterPropertiesSet() throws Exception;


3. ApplicationListener


ApplicationListener是Spring事件机制的一部分,与抽象类ApplicationEvent类配合来完成ApplicationContext的事件机制。

如果容器中存在ApplicationListener的Bean,当ApplicationContext调用publishEvent方法时,对应的Bean会被触发。这一过程是典型的观察者模式的实现。

源码如下:

@FunctionalInterface
public interface ApplicationListener<E extends ApplicationEvent> extends EventListener {

    /**
     * Handle an application event.
     * @param event the event to respond to
     */
    void onApplicationEvent(E event);

}


SqlSessionFactoryBean初始化源码分析
在简单了解了SqlSessionFactoryBean实现了三个接口后,接着来分析下,MyBatis-Spring是怎么通过SqlSessionFactoryBean注入SqlSessionFactory的。

首先是通过new的方式创建了SqlSessionFactoryBean对象,然后添加了一个数据源配置。

接着进入到getObject()方法,此时sqlSessionFactory属性为null,所以进入到afterPropertiesSet方法。

   

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


afterPropertiesSet会校验dataSource、sqlSessionFactoryBuilder不为空,configuration及configLocation 属性是否为null。

   

 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");
        this.sqlSessionFactory = this.buildSqlSessionFactory();
    }


校验通过后,进入到buildSqlSessionFactory方法,开始构建SqlSessionFactory并返回,该方法主要是读取SqlSessionFactoryBean相关的配置,解析为Configuration对象。

最终还是调用了build方法,通过配置创建DefaultSqlSessionFactory示例。

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


最后getObject()方法,返回添加了配置的SqlSessionFactory对象,并注入到IOC中。

总结


SqlSessionFactoryBean实际还是调用的SqlSessionFactoryBuilder的bulider方法,返回了SqlSessionFactory对象并注入到IOC中。如果不通过SqlSessionFactoryBean来获取SqlSessionFactory,那么我们需要各种添加配置,代码会变得很臃肿。

而SqlSessionFactoryBean为我们设置了很多默认配置,我们只需要注入数据库连接信息,就可以了。

BasicDataSource


1,连接池创建
BasicDataSource ->  DataSource
    @Override 
    public Connection getConnection()
        【a】 createDataSource()
              如果 dataSource不为空,则返回数据源对象,否则创建之,如下:
            【1】 createConnectionFactory()    
                    (a)通过配置参数 <property name="driverClassName" value="${jdbc.driver}" />,加载驱动类 Class.forName(driverClassName);
                    (b)通过配置参数 <property name="url" value="${jdbc.url}" />,获取驱动 DriverManager.getDriver(url);
                    (c)通过配置参数 <property name="username" value="${jdbc.username}" />, <property name="password" value="${jdbc.password}" />,
                    以及 driver,url,创建 数据库连接工厂 new DriverConnectionFactory(driver, url, connectionProperties);
            【2】 createConnectionPool()
                    (a)通过配置参数: <property name="maxActive" value="${dbcp.maxActive}" />
                                            <property name="maxIdle" value="${dbcp.maxIdle}" />
                                            <property name="minIdle" value="${dbcp.minIdle}" />
                                            等配置项,创建连接池org.apach.commons.pool.impl. GenericObjectPool  connectionPool
                                            commons-dbcp本身不创建连接池,通过commons-pool来管理连接池
                    (b)GenericObjectPool. addObject()中调用下步创建的连接池工厂类,创建连接,并通过 addObjectToPool(obj, false);将连接保存在连接池
            【4】 createPoolableConnectionFactory(driverConnectionFactory, statementPoolFactory, abandonedConfig)
                    (a)创建连接池工厂类 PoolableConnectionFactory,工厂类内部将该工厂设置到上步创建的 connectionPool中,这样就可以通过connectionPool中的 addObject()调用连接池工厂创建连接
            【5】 createDataSourceInstance()
                    (a)根据连接池connectionPool创建池化数据源对象  PoolingDataSource pds = new PoolingDataSource(connectionPool)
            【6】初始化连接
                    for (int i = 0 ; i < initialSize ; i++) {
                        connectionPool.addObject();
                    }
            【7】返回池化数据库连接对象 dataSource
        【b】 getConnection()
            【1】 _pool.borrowObject();调用【a】-【2】创建的连接池创建连接
                    (a) _factory.makeObject();调用【a】-【4】创建的连接池工厂类对象,返回 new PoolableConnection(conn,_pool,_config);对象
                            其中 PoolableConnection持有【a】-【2】创建的连接池 _pool ,当 PoolableConnection.close()时,该连接会被 _pool回收, _pool.returnObject(this);

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值