jdbc 分析一
其实jdbc用了很长时间了,一直就是使用
Class.forName(classname);
Connection conn=DriverManager.getConnection();
也一直就很奇怪,为什么这样就可以进行数据库连接了。
问题1.为什么一句Class.forName(drivername)以后就可以使用DriverManager来获取Connection了?
很多书上的解释是:Class.forName()会向DriverManager注册jdbc驱动程序。但是Class.forName()只是用来根据类的全限定名来加载类,并返回该类的Class而已。这是怎么回事?
刚下载了一个mysql的驱动程序com.mysql.jdbc.Driver,就拿它来分析一下
com.mysql.jdbc.Driver是这样定义的:
public class Driver extends NonRegisteringDriver implements java.sql.Driver {
static {
try {
java.sql.DriverManager.registerDriver(new Driver());
} catch (SQLException E) {
throw new RuntimeException("Can't register driver!");
}
}
public Driver() throws SQLException {
}
}
很简单啊,它实现了java.sql.Driver这个接口,并且继承了NonRegisteringDriver,NonRegisteringDriver或者它的超类一定实现了java.sql.Driver中定义的方法,因为com.mysql.jdbc.Driver并没有实现而且也不是abstract。
关键在于static块,它会在类被加载的时候执行。他生成了一个Driver的实例并且向DriverManager注册。啊哈,明白了为什么仅仅一句Class.forName(drivername)就可以注册jdbc驱动程序了。
问题2.DriverManager是怎样保存注册的jdbc驱动程序的呢?
现在肯定要看java.sql.DriverManager这个类了。这是一个无法生成实例的类,也无法被继承,因为它的构造函数是私有的,为的就是防止生成一个实例。它的所有的方法都是static的。当然,它的所有属性也是static的 ,因为既然无法生成实例,所以实例属性也就没有任何必要。
该类定义了一个DriverInfo类型的向量drivers用来存放已经注册的驱动程序。
private static java.util.Vector drivers = new java.util.Vector();
DriverInfo类的定义,也很简单,只是简单的记录驱动程序的实例,以及Class,以及该驱动程序类的全限定名。
class DriverInfo {
Driver driver;
Class driverClass;
String driverClassName;
public String toString() {
return ("driver[className=" + driverClassName + "," + driver + "]");
}
}
DriverManager.registerDriver是这样定义的:
public static synchronized void registerDriver(java.sql.Driver driver)
throws SQLException {
if (!initialized) {
initialize();
}
DriverInfo di = new DriverInfo();
di.driver = driver;
di.driverClass = driver.getClass();
di.driverClassName = di.driverClass.getName();
drivers.addElement(di);
println("registerDriver: " + di);
}
这个函数的内容也是一幕了然的。先不去管 if (!initialized) {initialize();},其余的部分,只是生成了一个DriverInfo,并且设定了DriverInfo的属性,再把它加入到drivers里面去。
再看看initialize(),原来它是用来处理使用jvm参数来指定驱动程序的情况。例如我可以通过java -Djdbc.drivers=com.mysql.jdbc.Driver来指定jdbc驱动程序。
那个println是一个类似日志的功能。可以通过设置logStream或者logWriter来改变日志的记录位置。
例如,我可以这样:
DriverManager.setLogStream(System.out);
Connection conn=DriverManager.getConnection("jdbc:mysql://localhost/test","user","pass");
conn.close();
java -Djdbc.drivers=com.mysql.jdbc.Driver=com.mysql.jdbc.Driver MyClass
执行结果:
DriverManager.getConnection("jdbc:mysql://localhost/test")
DriverManager.initialize: jdbc.drivers = com.mysql.jdbc.Driver
DriverManager.Initialize: loading com.mysql.jdbc.Driver
registerDriver: driver[className=com.mysql.jdbc.Driver,com.mysql.jdbc.Driver@5224ee]
JDBC DriverManager initialized
trying driver[className=com.mysql.jdbc.Driver,com.mysql.jdbc.Driver@5224ee]
getConnection returning driver[className=com.mysql.jdbc.Driver,com.mysql.jdbc.Driver@5224ee]
这样就可以看到jdbc驱动程序被加载的过程。
累了,先暂时看到这里吧,和我想的差不多,Driver果然是在加载类的时候用static块来执行某个函数来加载的。