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