Mybatis数据源模块 本篇学习Mybatis的数据源模块 org。apache。ibatis。datasource数据源模块要做什么 数据源模块要实现的功能:常见的数据源组件都实现了javax。sql。DataSource接口;MyBatis不但要能集成第三方的数据源组件,自身也提供了数据源的实现;一般情况下,数据源的初始化过程参数较多,比较复杂 首先我们创建dataSource的过程是非常复杂的。而且我们有需要方便扩展。因此我们可以使用工厂模式(创建型的模式) 工厂模式可以查看我之前写的一篇https:mp。toutiao。comprofilev4graphicpreview?pgcid7008076815566717447整个包结构 从这个包结构也可以看出来数据源模块整体的设计是一个工厂模式。unpooled:非连接池pooled:连接处jndi:容器连接如tomcat等。(这个我们本次不进行解析)源码解析 先来看下抽象工厂类:publicinterfaceDataSourceFactory{设置dataSource属性voidsetProperties(Propertiesprops);获取dataSourceDataSourcegetDataSource();}非连接池包 非连接池包里面一共就2个类UnpooledDataSourceFactoryUnpooledDataSource UnpooledDataSourceFactory是实现了抽象工厂DataSourceFactory的类。 UnpooledDataSource则是上面工厂类实际的调用的类 我们先看下UnpooledDataSourcepublicclassUnpooledDataSourceimplementsDataSource{数据库驱动privateClassLoaderdriverClassLoader;数据库连接相关配置信息privatePropertiesdriverProperties;驱动的注册中心privatestaticMapString,DriverregisteredDriversnewConcurrentHashMap();数据库连接的一些属性privateStringdriver;privateStringurl;privateStringusername;privateStringpassword;是否自动提交事务privateBooleanautoCommit;默认事务级别privateIntegerdefaultTransactionIsolationLevel;默认超时时间privateIntegerdefaultNetworkTimeout;使用静态代码快将驱动注册到DriverManage这个地方和MysqlDriver里面的原理大致一致static{EnumerationDriverdriversDriverManager。getDrivers();while(drivers。hasMoreElements()){Driverdriverdrivers。nextElement();registeredDrivers。put(driver。getClass()。getName(),driver);}}一大波构造函数和setter。。。获取conn通过用户名和密码privateConnectiondoGetConnection(Stringusername,Stringpassword)throwsSQLException{PropertiespropsnewProperties();if(driverProperties!null){props。putAll(driverProperties);}if(username!null){props。setProperty(user,username);}if(password!null){props。setProperty(password,password);}returndoGetConnection(props);}获取conn通过配置文件privateConnectiondoGetConnection(Propertiesproperties)throwsSQLException{initializeDriver();ConnectionconnectionDriverManager。getConnection(url,properties);设置事务是否自动提交,事务的隔离级别configureConnection(connection);returnconnection;}privatesynchronizedvoidinitializeDriver()throwsSQLException{if(!registeredDrivers。containsKey(driver)){Classlt;?driverType;try{if(driverClassLoader!null){driverTypeClass。forName(driver,true,driverClassLoader);}else{driverTypeResources。classForName(driver);}DriverManagerrequiresthedrivertobeloadedviathesystemClassLoader。http:www。kfu。comnsayerJavadynjdbc。htmlDriverdriverInstance(Driver)driverType。getDeclaredConstructor()。newInstance();DriverManager。registerDriver(newDriverProxy(driverInstance));registeredDrivers。put(driver,driverInstance);}catch(Exceptione){thrownewSQLException(ErrorsettingdriveronUnpooledDataSource。Cause:e);}}}配置超时时间、自动提交、事务privatevoidconfigureConnection(Connectionconn)throwsSQLException{if(defaultNetworkTimeout!null){conn。setNetworkTimeout(Executors。newSingleThreadExecutor(),defaultNetworkTimeout);}if(autoCommit!nullautoCommit!conn。getAutoCommit()){conn。setAutoCommit(autoCommit);}if(defaultTransactionIsolationLevel!null){conn。setTransactionIsolation(defaultTransactionIsolationLevel);}}。。。} 我们在继续看下工厂类UnpooledDataSourceFactoryauthorClintonBegin子类工厂对应的产品为UnpooledDataSourcepublicclassUnpooledDataSourceFactoryimplementsDataSourceFactory{privatestaticfinalStringDRIVERPROPERTYPREFIXdriver。;privatestaticfinalintDRIVERPROPERTYPREFIXLENGTHDRIVERPROPERTYPREFIX。length();protectedDataSourcedataSource;publicUnpooledDataSourceFactory(){this。dataSourcenewUnpooledDataSource();}OverridepublicvoidsetProperties(Propertiesproperties){PropertiesdriverPropertiesnewProperties();创建DataSource相应的metaObject,方便赋值MetaObjectmetaDataSourceSystemMetaObject。forObject(dataSource);遍历properties,将属性设置到DataSource中for(Objectkey:properties。keySet()){StringpropertyName(String)key;if(propertyName。startsWith(DRIVERPROPERTYPREFIX)){Stringvalueproperties。getProperty(propertyName);driverProperties。setProperty(propertyName。substring(DRIVERPROPERTYPREFIXLENGTH),value);}elseif(metaDataSource。hasSetter(propertyName)){Stringvalue(String)properties。get(propertyName);ObjectconvertedValueconvertValue(metaDataSource,propertyName,value);metaDataSource。setValue(propertyName,convertedValue);}else{thrownewDataSourceException(UnknownDataSourceproperty:propertyName);}}设置DataSource。driverProperties属性if(driverProperties。size()0){metaDataSource。setValue(driverProperties,driverProperties);}}OverridepublicDataSourcegetDataSource(){returndataSource;}。。。}连接池包 连接池包中类多了几个PooledDataSourceFactory:工厂类PoolState:用于管理PooledConnection对象状态的组件PooledConnection:数据库连接对象PooledDataSource:数据库连接池 直接贴源码分析: PooledDataSourceFactory继承了UnpooledDataSourceFactoryUnpooledDataSourceFactory实现了最基本的抽象工厂接口publicclassPooledDataSourceFactoryextendsUnpooledDataSourceFactory{publicPooledDataSourceFactory(){this。dataSourcenewPooledDataSource();}} PoolStateauthorClintonBeginPoolState:用于管理PooledConnection对象状态的组件,通过两个list分别,管理空闲状态的连接资源和活跃状态的连接资源并且提供了一些属性来记录时间次数等publicclassPoolState{protectedPooledDataSourcedataSource;空闲的连接池资源集合protectedfinalListPooledConnectionidleConnectionsnewArrayList();活跃的连接池资源集合protectedfinalListPooledConnectionactiveConnectionsnewArrayList();请求的次数protectedlongrequestCount0;累计的获得连接的时间protectedlongaccumulatedRequestTime0;累计的使用连接的时间。从连接取出到归还,算一次使用的时间;protectedlongaccumulatedCheckoutTime0;连接超时的次数protectedlongclaimedOverdueConnectionCount0;累计超时时间protectedlongaccumulatedCheckoutTimeOfOverdueConnections0;累计等待时间protectedlongaccumulatedWaitTime0;等待次数protectedlonghadToWaitCount0;连接失败次数protectedlongbadConnectionCount0;。。。} PooledConnectionauthorClintonBegin数据库连接对象采用了动态代理的方式classPooledConnectionimplementsInvocationHandler{privatestaticfinalStringCLOSEclose;privatestaticfinalClasslt;?〔〕IFACESnewClasslt;?〔〕{Connection。class};privatefinalinthashCode;记录当前连接所在的数据源对象,本次连接是由这个数据源创建的,关闭后也是回到这个数据源;可以理解为这个对象是提供给客户端使用的privatefinalPooledDataSourcedataSource;真实的连接对象privatefinalConnectionrealConnection;代理连接对象privatefinalConnectionproxyConnection;连接时间戳连接上数据源的时间privatelongcheckoutTimestamp;创建时间戳创建连接的时间privatelongcreatedTimestamp;最后一次使用时间戳privatelonglastUsedTimestamp;这个属性标志唯一连接池,由数据库url、用户名、密码生成一个hash值privateintconnectionTypeCode;连接是否还有效privatebooleanvalid;ConstructorforSimplePooledConnectionthatusestheConnectionandPooledDataSourcepassedin。paramconnectiontheconnectionthatistobepresentedasapooledconnectionparamdataSourcethedataSourcethattheconnectionisfrompublicPooledConnection(Connectionconnection,PooledDataSourcedataSource){this。hashCodeconnection。hashCode();this。realConnectionconnection;this。dataSourcedataSource;this。createdTimestampSystem。currentTimeMillis();this。lastUsedTimestampSystem。currentTimeMillis();this。validtrue;这里使用了动态代理来设置代理对象,这个代理对象最后在获取连接的时候会返回给调用Mybatis的一方this。proxyConnection(Connection)Proxy。newProxyInstance(Connection。class。getClassLoader(),IFACES,this);}。。。} PooledDataSource这个类是核心类了,获取和归还连接都是由这个类来完成的,我这边也仅对几个核心的方法进行分析publicclassPooledDataSourceimplementsDataSource{privatestaticfinalLoglogLogFactory。getLog(PooledDataSource。class);privatefinalPoolStatestatenewPoolState(this);数据源privatefinalUnpooledDataSourcedataSource;OPTIONALCONFIGURATIONFIELDS最大连接数protectedintpoolMaximumActiveConnections10;最大闲置连接数protectedintpoolMaximumIdleConnections5;最大的校验时间protectedintpoolMaximumCheckoutTime20000;最长等待时间protectedintpoolTimeToWait20000;最多几次允许几次无效连接protectedintpoolMaximumLocalBadConnectionTolerance3;测试连接是否有效的sql语句protectedStringpoolPingQueryNOPINGQUERYSET;是否允许测试连接protectedbooleanpoolPingEnabled;连接在这个配置时间内没有被使用,才允许测试连接是否有效protectedintpoolPingConnectionsNotUsedFor;连接池的唯一标志:根据数据库url、用户名、密码生成一个hash值privateintexpectedConnectionTypeCode;归还连接paramconnthrowsSQLExceptionprotectedvoidpushConnection(PooledConnectionconn)throwsSQLException{同样归还连接的时候也要加锁synchronized(state){先从活跃线程队列中移除state。activeConnections。remove(conn);if(conn。isValid()){判断闲置连接池资源是否已经达到上限if(state。idleConnections。size()poolMaximumIdleConnectionsconn。getConnectionTypeCode()expectedConnectionTypeCode){state。accumulatedCheckoutTimeconn。getCheckoutTime();if(!conn。getRealConnection()。getAutoCommit()){conn。getRealConnection()。rollback();}没有达到上线就创建新的连接(这个新连接实际上就是上面的归还的连接)放入空闲线程中PooledConnectionnewConnnewPooledConnection(conn。getRealConnection(),this);state。idleConnections。add(newConn);newConn。setCreatedTimestamp(conn。getCreatedTimestamp());newConn。setLastUsedTimestamp(conn。getLastUsedTimestamp());将连接对象设置为无效conn。invalidate();if(log。isDebugEnabled()){log。debug(ReturnedconnectionnewConn。getRealHashCode()topool。);}唤醒其他等待线程这个很重要,在我们获取连接的时候如果实在是没有连接就会让线程处于阻塞状态state。notifyAll();}else{state。accumulatedCheckoutTimeconn。getCheckoutTime();if(!conn。getRealConnection()。getAutoCommit()){conn。getRealConnection()。rollback();}如果闲置连接池已经达到上限了,将连接真实关闭conn。getRealConnection()。close();if(log。isDebugEnabled()){log。debug(Closedconnectionconn。getRealHashCode()。);}将连接对象设置为无效conn。invalidate();}}else{if(log。isDebugEnabled()){log。debug(Abadconnection(conn。getRealHashCode())attemptedtoreturntothepool,discardingconnection。);}state。badConnectionCount;}}}获取数据库连接paramusernameparampasswordreturnthrowsSQLException整个流程分为两部分1。获取连接2。对获取的连接做池化数据的信息处理第一部分:循环判断获取连接是否为空优先从空闲连接中获取连接有:空闲连接就进入第二部分没有:判断是否可以新增连接(活跃连接数是否已达到最大连接数)若没到最大线程数则直接创建俩姐进入第二部分若无法新增连接,则检查是否存在超时的连接,如果有就使用这个连接,没有则进入阻塞状态第二部分:修改线程的活跃线程数,请求次数等。(PoolState)privatePooledConnectionpopConnection(Stringusername,Stringpassword)throwsSQLException{booleancountedWaitfalse;PooledConnectionconnnull;longtSystem。currentTimeMillis();intlocalBadConnectionCount0;这里是一个循环,直到拿到conn,不然就会一致循环下去循环体中有超时和超次机制while(connnull){使用synchronized来处理多线程间的同步synchronized(state){检查是否还有空闲线程,不为空表示有if(!state。idleConnections。isEmpty()){Poolhasavailableconnection有空闲线程则将线程从线程池的空闲队列中拿出第一个线程(这里会减少空闲线程数量)connstate。idleConnections。remove(0);if(log。isDebugEnabled()){log。debug(Checkedoutconnectionconn。getRealHashCode()frompool。);}}else{当没有空闲线程时,判断活跃线程数是否已经达到最大线程数Pooldoesnothaveavailableconnectionif(state。activeConnections。size()poolMaximumActiveConnections){Cancreatenewconnection创建新的连接connnewPooledConnection(dataSource。getConnection(),this);if(log。isDebugEnabled()){log。debug(Createdconnectionconn。getRealHashCode()。);}}else{Cannotcreatenewconnection无法创建心对连接,则检查线程池中最早的线程是否超时。若超时则将连接释放出来PooledConnectionoldestActiveConnectionstate。activeConnections。get(0);longlongestCheckoutTimeoldestActiveConnection。getCheckoutTime();if(longestCheckoutTimepoolMaximumCheckoutTime){Canclaimoverdueconnectionstate。claimedOverdueConnectionCount;state。accumulatedCheckoutTimeOfOverdueConnectionslongestCheckoutTime;state。accumulatedCheckoutTimelongestCheckoutTime;state。activeConnections。remove(oldestActiveConnection);if(!oldestActiveConnection。getRealConnection()。getAutoCommit()){try{oldestActiveConnection。getRealConnection()。rollback();}catch(SQLExceptione){Justlogamessagefordebugandcontinuetoexecutethefollowingstatementlikenothinghappened。WrapthebadconnectionwithanewPooledConnection,thiswillhelptonotinterruptcurrentexecutingthreadandgivecurrentthreadachancetojointhenextcompetitionforanothervalidgooddatabaseconnection。Attheendofthisloop,bad{linkconn}willbesetasnull。log。debug(Badconnection。Couldnotrollback);}}在连接池中创建新的连接,注意对于数据库来说,并没有创建新连接;connnewPooledConnection(oldestActiveConnection。getRealConnection(),this);conn。setCreatedTimestamp(oldestActiveConnection。getCreatedTimestamp());conn。setLastUsedTimestamp(oldestActiveConnection。getLastUsedTimestamp());oldestActiveConnection。invalidate();if(log。isDebugEnabled()){log。debug(Claimedoverdueconnectionconn。getRealHashCode()。);}}else{Mustwait实在是没有连接可以用了,就阻塞等到try{if(!countedWait){state。hadToWaitCount;countedWaittrue;}if(log。isDebugEnabled()){log。debug(WaitingaslongaspoolTimeToWaitmillisecondsforconnection。);}longwtSystem。currentTimeMillis();state。wait(poolTimeToWait);state。accumulatedWaitTimeSystem。currentTimeMillis()wt;}catch(InterruptedExceptione){break;}}}}上面的循环已经拿到了conn这里做一个二次校验if(conn!null){pingtoserverandchecktheconnectionisvalidornot检测连接是否有效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。accumulatedRequestTimeSystem。currentTimeMillis()t;}else{如果连接无效if(log。isDebugEnabled()){log。debug(Abadconnection(conn。getRealHashCode())wasreturnedfromthepool,gettinganotherconnection。);}累计的获取无效连接次数1state。badConnectionCount;当前获取无效连接次数1localBadConnectionCount;connnull;拿到无效连接,但如果没有超过重试的次数,则重新走上面的while循环允许再次尝试获取连接,否则抛出异常if(localBadConnectionCount(poolMaximumIdleConnectionspoolMaximumLocalBadConnectionTolerance)){if(log。isDebugEnabled()){log。debug(PooledDataSource:Couldnotgetagoodconnectiontothedatabase。);}thrownewSQLException(PooledDataSource:Couldnotgetagoodconnectiontothedatabase。);}}}}}if(connnull){if(log。isDebugEnabled()){log。debug(PooledDataSource:Unknownsevereerrorcondition。Theconnectionpoolreturnedanullconnection。);}thrownewSQLException(PooledDataSource:Unknownsevereerrorcondition。Theconnectionpoolreturnedanullconnection。);}returnconn;}} 整体流程图: 壁纸图,侵权删