此处只讨论 加载驱动的问题,其他无关的问题我们暂时忽略
一般使用jdbc 连接数据库,手写代码如下
//jdk6之后,不需示执行Class.forName
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection connection = DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:orcl","xxx","xxxx");
我第一次写此代码时,就有很多疑问,但是后来也没研究,今天我把当时的疑问列出
1 "oracle.jdbc.driver.OracleDriver" 和"jdbc:oracle:thin" 有什么关系,为什么要写两遍
2 Class.forName 注册驱动后,没有返回值,是怎么回事
3 Class.forName 为什么 此行在jdk6版本中 又不需要了呢
4 DriverManager.getConnection怎么使用的驱动的呢
要回答上边的问题,我们先来看看这些代码的源码
1 要使用jdbc连接 oracle,必须先加载驱动,Class.forName就是加载驱动
Class.forName会初始化 oracle.jdbc.driver.OracleDriver这个类 (关于类的加载过程可自行搜索),初始化时自动执行类的静态代码块
// oracle.jdbc.driver.OracleDriver
static{
...
if (defaultDriver == null) {
defaultDriver = new oracle.jdbc.OracleDriver();
DriverManager.registerDriver(defaultDriver);
}
这样就驱动就注册了
2 jdk6为什么不需要 执行了Class.forName这行代码了呢
大家先了解一下ServiceLoader 。
简单来说就是在驱动jar包配置文件中,指定oracle.jdbc.driver.OracleDriver实现java.sql.Driver接口;然后DriverManager在初始化的时候,自动扫描所有jar包中实现了java.sql.Driver的类 ,并初始化 此实现类
//DriverManager
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
private static void loadInitialDrivers() {
...
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
//搜索服务的实现类(驱动实例)
ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
Iterator driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
driversIterator.next();// 此处初始化 并注册了
}
} catch(Throwable t) {
// Do nothing
}
return null;
//ServiceLoader
public S next() {
if (acc == null) {
return nextService();
} else {
...
//ServiceLoader
private S nextService() {
...
try {
c = Class.forName(cn, false, loader);
...
也就是说 DriverManger借助 ServiceLoader 找到驱动 并注册了,所以不需要再手工注册
3 驱动注册了,DriverManager中 驱动怎么被使用呢
//caller = Reflection.getCallerClass()
private static Connection getConnection(
String url, java.util.Properties info, Class> caller) throws SQLException {
....
ClassLoader callerCL = caller != null ? caller.getClassLoader() : null;
synchronized(DriverManager.class) {
// synchronize loading of the correct classloader.
if (callerCL == null) {
callerCL = Thread.currentThread().getContextClassLoader();
}
}
//callerCL 是方法调用类或者当前线程的classLoader,会在isDriverAllowed中使用
...
// 所有已经注册驱动都保存在registeredDrivers,这是个CopyOnWriteArrayList
for(DriverInfo aDriver : registeredDrivers) {
if(isDriverAllowed(aDriver.driver, callerCL)) {
try {
//jdbc:oracle:thin:@localhost:1521:orcl 最终是由驱动实现类使用
Connection con = aDriver.driver.connect(url, info);
...
private static boolean isDriverAllowed(Driver driver, ClassLoader classLoader) {
boolean result = false;
if(driver != null) {
Class> aClass = null;
try {
//使用不同的classLoader加载出来的驱动类是不相等的,此处就利用这点判断权限
aClass = Class.forName(driver.getClass().getName(), true, classLoader);
} catch (Exception ex) {
result = false;
}
result = ( aClass == driver.getClass() ) ? true : false;
}
return result;
}
遍历 registeredDrivers(所有已经注册驱动都在这里), 使用 DriverManager.getConnection方法所在类的classLoader(如果为空,则使用当前线程上下文的classLoaer),去加载驱动类 ,然后和registeredDrivers里边的比较 ,如果相等,则此驱动是有权限被 使用。
注意:此处 遍历 registeredDrivers时,只要找到一个有权限的,就立即返回。
如果使用相同方式注册多个了驱动,调用的是哪个驱动呢? registeredDrivers中第一个有权限且能正确连接上的的 ;那么程序里边 如果要连接多个数据库,使用jdbc怎么操作?