您的位置:首页 > 编程语言 > Java开发

JAVA JDBC(MySQL)驱动源码分析(三)

2010-03-30 18:14 459 查看
第二篇中讲解了如何装载驱动,以及它的初始化过程。

本篇分析一下连接数据库时使用的获取数据库连接的代码:

DriverManager.getConnection(“jdbc:mysql://localhost:3306/test”,”root”,”123”);


DriverManager.getConnection方法有三种重载方式,这里我们使用带有三个参数的方法,第一个表示数据库的URL, 第二、三个分别

是用户名和密码。获取数据库连接如此简单,只要把三个参数准确无误的写上去,连接肯定就能获取。但是连接方法中到底给我们做

了哪些工作呢? 下面找到DriverManager类的静态方法getConnection源码一探究竟。

public static Connection getConnection(String url,
String user, String password) throws SQLException {
java.util.Properties info = new java.util.Properties(); ///1
// Gets the classloader of the code that called this method, may
// be null.
ClassLoader callerCL = DriverManager.getCallerClassLoader(); ///2
if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}
return (getConnection(url, info, callerCL)); ///3
}


1处定义了一个Properties对象,java.util.Properties它是用来在文件中存储键值对的,其中键和值是用等号分隔的。可以将值以

key-value的形式存入Properties.

2处调用方法getCallerClassLoader得到类装载器对象, 这个地方有点意思,既然是调用方法,怎么变成了下面这种形式呢?根本就

不见方法体。

源码:

private static native ClassLoader getCallerClassLoader();


getCallerClassLoader()是一个静态原生方法,返回类型为ClassLoader, 被声明为native, 说明这个方法是一个原生方法,也就是说

这个方法是用C/C++语言实现的,并且被编译成了DLL,由JAVA调用。这些函数的实体在DLL中,JDK源码并不包含,在JAVA源文件中是

找不到源代码的。不同的平台其实现也有所差异。这也是JAVA的底层机制,实际上JAVA就是在不同的平台上调用不同的native方法实

现对操作系统的访问。native关键字一般是和C/C++联合开发的时候使用。如果标明为native 要求运行时通知操作系统,这个函数必

须给我实现,JAVA需要调用。如果未实现,那么调用时会抛出一个异常java.lang.UnsatisfiedLinkError

接下来判断用户名和密码,并将其存放到Properties对象中:

if (user != null) {
info.put("user", user);
}
if (password != null) {
info.put("password", password);
}


调用获取连接的另一个重载方法

getCollection(String url, java.util.Properties info, ClassLoader callerCL)

return (getConnection(url, info, callerCL));


getConnection(url, info, callerCL) 源码:

private static Connection getConnection(
String url, java.util.Properties info, ClassLoader callerCL) throws SQLException {
java.util.Vector drivers = null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if(callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}

if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}

println("DriverManager.getConnection(/"" + url + "/")");

if (!initialized) {
initialize();
}
synchronized (DriverManager.class){
// use the readcopy of drivers
drivers = readDrivers;
}
// Walk through the loaded drivers attempting to make a connection.
// Remember the first exception that gets raised so we can reraise it.
SQLException reason = null;
for (int i = 0; i < drivers.size(); i++) {
DriverInfo di = (DriverInfo)drivers.elementAt(i);

// If the caller does not have permission to load the driver then
// skip it.
if ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) {
println("    skipping: " + di);
continue;
}
try {
println("    trying " + di);
Connection result = di.driver.connect(url, info);
if (result != null) {
// Success!
println("getConnection returning " + di);
return (result);
}
} catch (SQLException ex) {
if (reason == null) {
reason = ex;
}
}
}

// if we got here nobody could connect.
if (reason != null)    {
println("getConnection failed: " + reason);
throw reason;
}

println("getConnection: no suitable driver found for "+ url);
throw new SQLException("No suitable driver found for "+ url, "08001");
}


此方法代码较多,我们一步步来分析。

1、 传参

从前面的那个getConnection方法中形成的三个参数传递到这个getConnection方法中, 参数包括连接数据库的URL,包装了用户名和

密码的对象info, 通过调用原生函数返回的类装载器callerCL

2、 同步DriverManager.class

同步DriverManager的Class对象,synchronized同步的对象为DriverManager.class,是为同步正确的类加载器来加载类,在同步块

中判断传入的类装载器对象是否存在,如果为null, 通过当前线程来获取上下文类装载器,保证JDBC驱动程序类以外的rt.jar中的类

可以在这里被加载。有关于Thread和synchronized读者可以参考java多线程编程

synchronized(DriverManager.class) {
if(callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}


3、 判断URL,如果为null则抛出SQLException异常

判断initialized的值,如果未初始化,继续调用初始化方法,此处在第二篇中已详细解释初始化过程,初始化之后writeDrivers 和readDrivers 将会有系统所有驱动数据,接下来使用synchronized同步DriverManager.class对象, 将方法中定义的集合drivers 引用 readDrivers对象,readDrivers是从writeDrivers拷贝过来

if(url == null) {
throw new SQLException("The url cannot be null", "08001");
}
println("DriverManager.getConnection(/"" + url + "/")");
if (!initialized) {
initialize();
}
synchronized (DriverManager.class) {
// use the readcopy of drivers
drivers = readDrivers;
}


4、 遍历驱动

通过for循环,遍历drivers集合,其中每个元素的类型为DriverInfo, 这个在第二篇中也有详细描述

首先取得集合中的每一个对象元素,调用getCallerClass()方法

for (int i = 0; i < drivers.size(); i++) {
DriverInfo di = (DriverInfo)drivers.elementAt(i);
if ( getCallerClass(callerCL, di.driverClassName ) != di.driverClass ) {
println("    skipping: " + di);
continue;
}
……
}


getCallerClass方法源码:

private static Class getCallerClass(ClassLoader callerClassLoader,
String driverClassName) {
Class callerC = null;
try {
callerC = Class.forName(driverClassName, true, callerClassLoader);
}
catch (Exception ex) {
callerC = null;
}
return callerC;
}


这个方法返回一个Class对象,通过指定的类装载器来装载驱动类。这个方法内做得非常小心,如果出现异常都会把需要返

回的Class对象置为null.

在if语句中调用getCallerClass方法得到的Class 对象和每一个驱动的Class对象比较,不相等的话就继续执行下一次循环

,否则都调用Driver的connect方法,传入url, 和 info,通过这个方法返回Connection连接对象

Connection result = di.driver.connect(url, info);


Driver接口的实现类中的connect方法具体所做的工作将在下一篇中详述
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: