mysql-connector-java源码解析(⼀)
前⾔:
mysql作为常⽤的数据库,笔者⼀直以来都觉得⽐较神秘。
在实际应⽤中,我们可以通过其Navicat、mysql command-line、java-mysql-jar等⽅式来与其进⾏进⾏连接操作。
在笔者接触到越来越多的中间件应⽤后,mysql的神秘⾯纱就被慢慢揭开了。故决定将这个过程通过博客的⽅式记录下来,并且当前篇⽂章作为Mycat的前序篇,对于需要学习Mycat的同学来说,也是有必要去了解的。
本⽂主要通过对mysql-connector-java源码的分析来了解client与mysql-server创建连接握⼿的基本过程。
1.准备环境
* Windows环境下的mysql服务安装(笔者安装版本为5.7.17)
* jdk1.8.0_131
* mysql-connector-java-5.1.35.jar
* 简单表结构(student)
CREATE TABLE student  (
id int(11) NOT NULL AUTO_INCREMENT,
sno varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
sname varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL,
PRIMARY KEY (id) USING BTREE
);
2.代码准备
通过最原始的JDBC的⽅式来创建于mysql-server的连接。
// 创建mysql连接基本类
public class DBConn {
private static final String jdbcdriver="sql.jdbc.Driver";
// origin
private static final String jdbcurl="jdbc:mysql://localhost:3306/db1?useUnicode=true&characterEncoding=utf-8";
private static final String username="root";
private static final String password="root";
private static final String driver = "sql.jdbc.Driver";
private static final Connection conn = null;
/**
* 连接数据库
* @return
*/
public static Connection conn() {
Connection conn = null;
try {
Class.forName(driver);
try {
conn = Connection(jdbcurl, username, password);
} catch (SQLException e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
return conn;
}
/**
* 关闭数据库链接
* @return
*/
public static void close() {
if(conn != null) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
本⽂主要就是对Connection(jdbcurl, username, password)的源码解析
3.创建连接过程分析
3.1 Class.forName(driver)
此时的driver即是sql.jdbc.Driver,Class.forName不需要多介绍,java的⼀种类加载⽅式,使⽤装载当前类的类加载器来装载指定的class类。
该步骤的意义就是将mysql的Driver加载到JVM中。并且会执⾏Driver的static⽅法,源码如下:
// sql.jdbc.Driver
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
public Driver() throws SQLException {
}
static {
try {
// 加载后,会默认执⾏
} catch (SQLException var1) {
throw new RuntimeException("Can't register driver!");
}
}
}
// isterDriver
public static synchronized void registerDriver(java.sql.Driver driver,
DriverAction da)
throws SQLException {
// CopyOnWriteArrayList<DriverInfo> registeredDrivers = new CopyOnWriteArrayList<>()
// 直接将Driver包装成DriverInfo添加到DriverManger中
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
}
...
}
总结:执⾏完Class.forName("sql.jdbc.Driver")后,该Driver对象就被添加到DriverManger中
3.2 getConnection
下⾯就是最关键的这⼀句代码,Connection(jdbcurl, username, password);通⽤的这句创建Connection的代码,可以创建不同Driver类型的连接,还是蛮神奇的,下⾯我们就来⼀起看下吧。
// Connection(String url,String user, String password)
public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties();
// 拼装⽤户名密码到info中
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, CallerClass()));
}
// Connection(String url, java.util.Properties info, Class<?> caller)
private static Connection getConnection(
String url, java.util.Properties info, Class<?> caller) {
...
for(DriverInfo aDriver : registeredDrivers) {
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
// 主要就是这⼀句在创建连接
// 本质上还是调⽤了具体使⽤的Driver的connect⽅法
// 本质上还是调⽤了具体使⽤的Driver的connect⽅法
/
/ 所以就是调⽤t(String url, Properties info)实现
Connection con = t(url, info);
if (con != null) {
// Success!
println("getConnection returning " + Class().getName());
return (con);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
mysql下载jar包}
...
}
...
}
// t(String url, Properties info)
public Connection connect(String url, Properties info) throws SQLException {
...
Properties props = null;
if ((props = this.parseURL(url, info)) == null) {
return null;
} else if (!"1".Property("NUM_HOSTS"))) {
tFailover(url, info);
} else {
try {
// 在这⾥创建Connection
= Instance(this.host(props), this.port(props), props, this.database(props), url);
return newConn;
...
}
}
/
/ Instance
protected static Connection getInstance(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url) throws SQLExce    return (Connection)(!Util.isJdbc4() ?
new ConnectionImpl(hostToConnectTo, portToConnectTo, info, databaseToConnectTo, url) :
// 看JDBC版本,笔者这⾥是JDBC4,故使⽤sql.jdbc.JDBC4Connection来连接
(Connection)Util.handleNewInstance(JDBC_4_CONNECTION_CTOR, new Object[]{hostToConnectTo, portToConnectTo, info, databaseToConnectTo, url}, }
// 5.mysql.jdbc.JDBC4Connection实例创建
public class JDBC4Connection extends ConnectionImpl {
// JDBC4Connection中的构造⽅法直接使⽤ConnectionImplement的构造⽅法
}
// 6.mysql.jdbc.ConnectionImpl构造⽅法
public ConnectionImpl(String hostToConnectTo, int portToConnectTo, Properties info, String databaseToConnectTo, String url) throws SQLException {
...
this.port = portToConnectTo;
this.database = databaseToConnectTo;
this.user = Property("user");
this.password = Property("password");
...
try {
this.dbmd = MetaData(false, false);
this.initializeSafeStatementInterceptors();
// 在这⾥创建socket连接
this.unSafeStatementInterceptors();
} catch (SQLException var11) {
...
}
}
// ateNewIO
public void createNewIO(boolean isForReconnect) throws SQLException {
ConnectionMutex()) {
Properties mergedProps = poseAsProperties(this.props);
/
/ 是否⾼可⽤,如果配置了⾼可⽤,针对不可⽤时会重试多次,本⽂中分析⼀次连接即可
if (!HighAvailability()) {
} else {
}
}
}
// tOneTryOnly(boolean isForReconnect, Properties mergedProps)
private void connectOneTryOnly(boolean isForReconnect, Properties mergedProps) throws SQLException {
Object var3 = null;
try {
// 核⼼连接⽅法
// 赋值其他参数
this.isClosed = false;
boolean oldAutoCommit = AutoCommit();
int oldIsolationLevel = this.isolationLevel;
boolean oldReadOnly = this.isReadOnly(false);
String oldCatalog = Catalog();
this.io.setStatementInterceptors(this.statementInterceptors);
this.initializePropsFromServer();
if (isForReconnect) {
this.setAutoCommit(oldAutoCommit);
if (this.hasIsolationLevels) {
this.setTransactionIsolation(oldIsolationLevel);
}
this.setCatalog(oldCatalog);
this.setReadOnly(oldReadOnly);
}
...
// Connect(Properties mergedProps)
private void coreConnect(Properties mergedProps) throws SQLException, IOException {
...
this.port = newPort;
this.host = newHost;
this.sessionMaxRows = -1;
// 创建长连接
this.io = new MysqlIO(newHost, newPort, mergedProps, SocketFactoryClassName(), Proxy(), SocketTimeout(), this.largeRowSizeThresh    // 三次握⼿完成后,进⾏信息交换,完成⽤户名密码验证
this.io.doHandshake(this.user, this.password, this.database);
}
3.3 MySQLIO
从3.2的代码流程来看,最终创建长连接的代码在于MysqlIO的构造⽅法中。下⾯我们来详细看下

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。