• 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);
          }
        }
      }
    

      

     

     
  • 相关阅读:
    spring mvc 数据回显
    spring mvc 数据校验
    如何构建高性能web站点之:分布式缓存
    面霸吕国栋之:整理的一些面试题
    项目中我为什么用Mongodb取代Mysql
    对mysql存储性能优化的基本理解
    SQL语句面试题(IN、NOT IN、COUNT、GROUP BY)
    java面试题
    java中的网络通信编程之UDP篇
    java中的网络通信编程之TCP篇
  • 原文地址:https://www.cnblogs.com/linlf03/p/12587084.html
Copyright © 2020-2023  润新知