Mybatis原生DataSource源码解析

我们以Mybatis原生源码解析创建Demo进行DataSource源码的解析

一、上一篇介绍到环境的搭建

1、配置文件 Configure.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>
    <typeAliases>
        <typeAlias alias="User2" type="com.example.mybatis.model.User" />
    </typeAliases>
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"></transactionManager>
            <dataSource type="POOLED">
                <property name="driver"  value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://127.0.0.1:3306/mybatis"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper resource="mapper/User.xml" />
    </mappers>
</configuration>

  

2、测试代码

public class MyBatisTest1 {

    private static SqlSessionFactory sqlSessionFactory;
    private static Reader reader;

    static {
        try {
            // 1.读取mybatis配置文件,并生成SQLSessionFactory
            reader = Resources.getResourceAsReader("config/Configure.xml");
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static  SqlSessionFactory getSession(){
        return  sqlSessionFactory;
    }

    public static void main(String[] args) {
        // 2.获取session,主要的CRUD操作均在SqlSession中提供
        SqlSession session = sqlSessionFactory.openSession();
        try {
            // 3.执行查询操作
            // 该方法包括三个步骤:封装入参数、执行查询、封装结果为对象类型
           User user =  (User)session.selectOne("com.example.mybatis.dao.UserMapper.getUserById",1 ) ;
           if(user != null){
               System.out.println("name is : " + user.getName() + ",所属部门: " + user.getDept());
           }
        }finally {
            session.close();
        }
    }
}  

由上面可知,通过如下代码,创建了sqlSessionFactory  

 reader = Resources.getResourceAsReader("config/Configure.xml");
 sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);  

通过sqlSessionFactory获得sqlSession,然后又session进行数据库的CRUD操作

由上篇Mybatis原生源码解析可知,在创建sqlSessionFactory的时候,就好创建对应DataSource,并放到Configuration中。下面就看下DataSource是怎么被创建的。  

 2、DataSource

SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

  

1) build(reader);  

  public SqlSessionFactory build(Reader reader) {
    return build(reader, null, null);
  }

  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
    try {
      //关键是这两行代码
      XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
      return build(parser.parse());
    } catch (Exception e) {
      throw ExceptionFactory.wrapException("Error building SqlSession.", e);
    } finally {
      ErrorContext.instance().reset();
      try {
        reader.close();
      } catch (IOException e) {
        // Intentionally ignore. Prefer previous error.
      }
    }
  }

  

2) new XMLConfigBuilder

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
    //1、创建Configuration
    super(new Configuration());
    ErrorContext.instance().resource("SQL Mapper Configuration");
    //2、赋值
    this.configuration.setVariables(props);
    this.parsed = false;
    this.environment = environment;
    this.parser = parser;
  }

//创建Configuration
  public Configuration() {
    typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
    typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);

    //这里针对不同类型的DataSource 做了映射。DataSource类型有三种:JNDI,POOLED,UNPOOLED,分别有对应的工厂类实现
    typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
    typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
    typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);

    typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
    typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
    typeAliasRegistry.registerAlias("LRU", LruCache.class);
    typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
    typeAliasRegistry.registerAlias("WEAK", WeakCache.class);

    typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);

    typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
    typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);

    typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
    typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
    typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
    typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
    typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
    typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
    typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);

    typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
    typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);

    languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
    languageRegistry.register(RawLanguageDriver.class);
  }

  

  

3) parser.parse()

public Configuration parse() {
    if (parsed) {
      throw new BuilderException("Each XMLConfigBuilder can only be used once.");
    }
    parsed = true;
    parseConfiguration(parser.evalNode("/configuration"));
    return configuration;
  }

  private void parseConfiguration(XNode root) {
    try {
      propertiesElement(root.evalNode("properties")); //issue #117 read properties first
      typeAliasesElement(root.evalNode("typeAliases"));
      pluginElement(root.evalNode("plugins"));
      objectFactoryElement(root.evalNode("objectFactory"));
      objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
      settingsElement(root.evalNode("settings"));
      //对environments进行解析
      environmentsElement(root.evalNode("environments")); // read it after objectFactory and objectWrapperFactory issue #631
      databaseIdProviderElement(root.evalNode("databaseIdProvider"));
      typeHandlerElement(root.evalNode("typeHandlers"));
      mapperElement(root.evalNode("mappers"));
    } catch (Exception e) {
      throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
    }
  }


//XMLConfigBuilder.environmentsElement()
  private void environmentsElement(XNode context) throws Exception {
    if (context != null) {
      if (environment == null) {
        environment = context.getStringAttribute("default");
      }
      for (XNode child : context.getChildren()) {
        String id = child.getStringAttribute("id");
        if (isSpecifiedEnvironment(id)) {
          TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
          //这两行代码就是关于DataSource的生成
          DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
          DataSource dataSource = dsFactory.getDataSource();
          Environment.Builder environmentBuilder = new Environment.Builder(id)
              .transactionFactory(txFactory)
              .dataSource(dataSource);
          configuration.setEnvironment(environmentBuilder.build());
        }
      }
    }
  }

//XMLConfigBuilder.dataSourceElement
  private DataSourceFactory dataSourceElement(XNode context) throws Exception {
    if (context != null) {
      //当前type为POOLED
      String type = context.getStringAttribute("type");
      Properties props = context.getChildrenAsProperties();
      //通过resolveClass(type)获得对应的class。
      //通过反射获得工厂类实例
      //DataSourceFactory就是PooledDataSourceFactory
      DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
      factory.setProperties(props);
      return factory;
    }
    throw new BuilderException("Environment declaration requires a DataSourceFactory.");
  }

  

  

4)DataSource dataSource = dsFactory.getDataSource(); 实际上数据源为PooledDataSource

  public PooledDataSourceFactory() {
    this.dataSource = new PooledDataSource();
  }

 

3、PooledDataSource与Connection

  public Connection getConnection() throws SQLException {
    //分两步 1、popConnection 和 getProxyConnection
    return popConnection(dataSource.getUsername(), dataSource.getPassword()).getProxyConnection();
  }

  

1) popConnection 

  private PooledConnection popConnection(String username, String password) throws SQLException {
    boolean countedWait = false;
    PooledConnection conn = null;
    long t = System.currentTimeMillis();
    int localBadConnectionCount = 0;

    while (conn == null) {
      synchronized (state) {
        //1、如果有空闲的连接  
        if (state.idleConnections.size() > 0) {
          // 直接从空闲连接中获取一个连接并返回
          conn = state.idleConnections.remove(0);
          if (log.isDebugEnabled()) {
            log.debug("Checked out connection " + conn.getRealHashCode() + " from pool.");
          }
        } else {
          // 2、没有空闲连接的话。 如果没有超过最大连接数,则直接创建一个Connection
          if (state.activeConnections.size() < poolMaximumActiveConnections) {
            // Can create new connection
            conn = new PooledConnection(dataSource.getConnection(), this);
            @SuppressWarnings("unused")
            //used in logging, if enabled
            Connection realConn = conn.getRealConnection();
            if (log.isDebugEnabled()) {
              log.debug("Created connection " + conn.getRealHashCode() + ".");
            }
          } 
	  //3、如果超过了最大连接数,则不能创建新的连接。
	  else {
            // Cannot create new connection
            PooledConnection oldestActiveConnection = state.activeConnections.get(0);
            long longestCheckoutTime = oldestActiveConnection.getCheckoutTime();
	    //如果最先创建的连接已经超时使用了,则移除这个连接。不是自动提交,则rollback.
	    //然后建立一个新的连接
            if (longestCheckoutTime > poolMaximumCheckoutTime) {
              // Can claim overdue connection
              state.claimedOverdueConnectionCount++;
              state.accumulatedCheckoutTimeOfOverdueConnections += longestCheckoutTime;
              state.accumulatedCheckoutTime += longestCheckoutTime;
              state.activeConnections.remove(oldestActiveConnection);
              if (!oldestActiveConnection.getRealConnection().getAutoCommit()) {
                oldestActiveConnection.getRealConnection().rollback();
              }
              conn = new PooledConnection(oldestActiveConnection.getRealConnection(), this);
              oldestActiveConnection.invalidate();
              if (log.isDebugEnabled()) {
                log.debug("Claimed overdue connection " + conn.getRealHashCode() + ".");
              }
            } 
	      //4、如果没有超时的连接,只能等待
	      else {
	      	
              // Must wait
              try {
                if (!countedWait) {
                  state.hadToWaitCount++;
                  countedWait = true;
                }
                if (log.isDebugEnabled()) {
                  log.debug("Waiting as long as " + poolTimeToWait + " milliseconds for connection.");
                }
                long wt = System.currentTimeMillis();
                state.wait(poolTimeToWait);
                state.accumulatedWaitTime += System.currentTimeMillis() - wt;
              } catch (InterruptedException e) {
                break;
              }
            }
          }
        }
	//5、对获得到的Connection进行赋值操作,如果连接无效,超过移除,则抛出异常
        if (conn != null) {
          if (conn.isValid()) {
            if (!conn.getRealConnection().getAutoCommit()) {
              conn.getRealConnection().rollback();
            }
            conn.setConnectionTypeCode(assembleConnectionTypeCode(dataSource.getUrl(), username, password));
            conn.setCheckoutTimestamp(System.currentTimeMillis());
            conn.setLastUsedTimestamp(System.currentTimeMillis());
            state.activeConnections.add(conn);
            state.requestCount++;
            state.accumulatedRequestTime += System.currentTimeMillis() - t;
          } else {
            if (log.isDebugEnabled()) {
              log.debug("A bad connection (" + conn.getRealHashCode() + ") was returned from the pool, getting another connection.");
            }
            state.badConnectionCount++;
            localBadConnectionCount++;
            conn = null;
            if (localBadConnectionCount > (poolMaximumIdleConnections + 3)) {
              if (log.isDebugEnabled()) {
                log.debug("PooledDataSource: Could not get a good connection to the database.");
              }
              throw new SQLException("PooledDataSource: Could not get a good connection to the database.");
            }
          }
        }
      }

    }

    if (conn == null) {
      if (log.isDebugEnabled()) {
        log.debug("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
      }
      throw new SQLException("PooledDataSource: Unknown severe error condition.  The connection pool returned a null connection.");
    }

    return conn;
  }  

由上面的分析可知,所谓的连接池就是一个创建好的连接集合,如果结合不为空,则直接从集合中获取连接使用;

如果集合为空,且当前连接数不超过最大连接数,则直接创建连接,否则只能等待。

2)创建连接 new PooledConnection(dataSource.getConnection(), this)

第一步dataSource.getConnection()

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

//UnpooledDataSource.doGetConnection() 
  private Connection doGetConnection(String username, String password) throws SQLException {
    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);
  }

//UnpooledDataSource.doGetConnection()
  private Connection doGetConnection(Properties properties) throws SQLException {
    initializeDriver();
    //最终使用DriverManager来创建连接
    Connection connection = DriverManager.getConnection(url, properties);
    configureConnection(connection);
    return connection;
  }

  

第二步new PooledConnection(dataSource.getConnection(), this)

  public PooledConnection(Connection connection, PooledDataSource dataSource) {
    this.hashCode = connection.hashCode();
    this.realConnection = connection;
    this.dataSource = dataSource;
    this.createdTimestamp = System.currentTimeMillis();
    this.lastUsedTimestamp = System.currentTimeMillis();
    this.valid = true;
    //使用代理生成pooledConnection连接
    this.proxyConnection = (Connection) Proxy.newProxyInstance(Connection.class.getClassLoader(), IFACES, this);
  }

  

这里为什么要获得代理类?

class PooledConnection implements InvocationHandler 

 我们看下里面的invoke方法 

  public Object invoke(Object proxy, Method method, Object[] args)
      throws Throwable {
    String methodName = method.getName();
    //如果调用了close方法,则实际执行的是将连接放回连接池
    if (CLOSE.hashCode() == methodName.hashCode() && CLOSE.equals(methodName)) {
      dataSource.pushConnection(this);
      return null;
    } else {
      try {
        if (method.getDeclaringClass() != Object.class) {
          // issue #578. 
          // toString() should never fail
          // throw an SQLException instead of a Runtime
          checkConnection();
        }
	//其它的操作交给realConnection
        return method.invoke(realConnection, args);
      } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
      }
    }
  }

  

 

 

猜你喜欢

转载自www.cnblogs.com/linlf03/p/12587084.html