我们以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); } } }