MyBatis源码解析(三):构建DataSource

上一篇文章,还剩 environments对数据源解析,事务配置解析 

<!-- 可以配置多个运行环境,但是每个 SqlSessionFactory 实例只能选择一个运行环境常用: 
一、development:开发模式 
二、work:工作模式 -->
<environments default="development">
    <!--id属性必须和上面的default一样 -->
    <environment id="development">
        <!--使用JDBC的事务管理机制-->
        <transactionManager type="JDBC" />
        <dataSource type="POOLED">
        
        <!--UNPOOLED:(不使用连接池)-->
        <!--POOLED:(使用连接池)-->
        <!--JNDI :集中或在外部配置数据源,然后放置一个 JNDI 上下文(EJB等使用)-->
        
            <property name="driver" value="${jdbc.driver}" />
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </dataSource>
    </environment>
</environments>
//1.2.4
private void environmentsElement(XNode context) throws Exception {
    /**
     * environments节点标签不为空才解析,
     * 我们也可以不在 mybatis-configuration.xml 文件中配置<environments/>标签,交给spring管理
     */
    if (context != null) {
      //如果 environment 值为 null,
      if (environment == null) {
        //获取<environments default="属性值">中的default属性值
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        /**
         * 循环遍历子标签<environment />
         */
        //获取<environment id="属性值">中的id属性值
        String id = child.getStringAttribute("id");
        //遍历所有<environment>的时候一次判断相应的id是否是default设置的值
        if (isSpecifiedEnvironment(id)) {
          
          //1.2.4.1 获取配置的事务管理器
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          
          //1.2.4.2  构建通过type对应的DataSourceFactory 
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));

          //1.2.4.3  通过type对应的类型的DataSourceFactory构建DataSource 
          DataSource dataSource = dsFactory.getDataSource();
          Environment.Builder environmentBuilder = new Environment.Builder(id)
            .transactionFactory(txFactory)
            .dataSource(dataSource);
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

  private boolean isSpecifiedEnvironment(String id) {
    if (environment == null) {
      throw new BuilderException("No environment specified.");
    } else if (id == null) {
      throw new BuilderException("Environment requires an id attribute.");
    } else if (environment.equals(id)) {
      return true;
    }
    return false;
  }
/**
   * 1.2.4.2  构建DataSourceFactory
   *
   * @param context
   * @return
   * @throws Exception
   */
  private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {

      //根据type,创建不同的DataSourceFactory
      String type = context.getStringAttribute("type");
      
      //解析数据库连接信息,保存至props(Properties继承Hashtable)
      Properties props = context.getChildrenAsProperties();
      
      //通过type类型,构建不同类型的DataSourceFactory(工厂模式)
      //通过type(别名)从Map<String, Class<?>> typeAliases = new HashMap<>();获取对应的DataSourceFactory
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
      
      //将数据库连接信息的值设置到DataSourceFactory
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  }

1.2.4.3  通过type对应的类型的DataSourceFactory构建DataSource 

通过type构建不同的DataSourceFactory ,构建对应的DataSource

PooledDataSourceFactory--> PooledDataSource
UnpooledDataSourceFactory--> UnpooledDataSource

UnPooledDataSource

不同类型的DataSource在获取连接时的方式不一

UnPooledDataSource 的 getConnection() 方法实现:

public Connection getConnection() throws SQLException {
        return doGetConnection(username, password);
    }

    public Connection getConnection(String username, String password) throws SQLException {
        return doGetConnection(username, password);
    }
    private Connection doGetConnection(String username, String password) throws SQLException {
        //将数据连接信息、驱动都封装到Properties文件中
        Properties props = new Properties();
        if (driverProperties != null) {
            props.putAll(driverProperties);
        }
        if (username != null) {
            props.setProperty("user", username);
        }
        if (password != null) {
            props.setProperty("password", password);
        }
        return doGetConnection(props);
    }

    /**
     * 获取数据库连接
     */
    private Connection doGetConnection(Properties properties) throws SQLException {
        //1、初始化驱动
        initializeDriver();
        //2、从DriverManager中获取连接,获取新的Connection对象
        Connection connection = DriverManager.getConnection(url, properties);
        //3、配置connection属性
        configureConnection(connection);
        return connection;
    }

使用 UnpooledDataSource 类型的数据源,每次需要连接的时候都会调用 getConnection() 即每次都需要经过以下步骤。在底层就相当于和数据库建立的通信连接,在建立通信连接的过程会消耗一些资源

        //1、初始化驱动
        initializeDriver();
        //2、从DriverManager中获取连接,获取新的Connection对象
        Connection connection = DriverManager.getConnection(url, properties);
        //3、配置connection属性
        configureConnection(connection);
        return connection;

PooledDataSource

//空闲状态的连接集合
  protected final List<PooledConnection> idleConnections = new ArrayList<>();
  //活动状态的连接集合
  protected final List<PooledConnection> activeConnections = new ArrayList<>();

PooledDataSource将java.sql.Connection对象包裹成PooledConnection对象放到了PoolState类型的容器中维护。 PooledConnection的 空闲状态(idle)和活动状态(active),分别被存储到PoolState容器内的idleConnections和activeConnections两个List集合中。

  • 获取连接

调用PooledDataSource的getConnection()方法时,会优先从idleConnections集合中取PooledConnection对象,如果没有,则activeConnections集合是否已满,如果未满,PooledDataSource会创建出一个PooledConnection,添加到此集合中,并返回。

  • 释放连接:

使用动态代理,真正的Connection对象创建一个代理对象,代理对象所有的方法都是调用相应的真正Connection对象的方法实现。当代理对象执行close()方法时,要特殊处理,不调用真正Connection对象的close()方法,而是将Connection对象添加到连接池中。。

在调用con.close()方法的时候,不真正调用close()方法,将其换成将Connection对象放到连接池容器中的代码!

class PooledConnection implements InvocationHandler {
    @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
    //动态代理调用close方法。
    String methodName = method.getName();
    if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
      
      //从活动状态的连接集合中移除,添加到空闲状态的集合中
      dataSource.pushConnection(this);
      return null;
    }
    try {
      if (!Object.class.equals(method.getDeclaringClass())) {
        // issue #579 toString() should never fail
        // throw an SQLException instead of a Runtime
        checkConnection();
      }
      return method.invoke(realConnection, args);
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }

  }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值