文章目录
问题
- 今天在加载Oracle数据库时发现不用Class.forName加载驱动也能获取数据库的连接对象,经过一番查询,终于明白其中原因
Java中的加载驱动的三种方法
一、使用Class.forName(“驱动类的全路径名”);
String driverClass = "oracle.jdbc.driver.OracleDriver";
Class.forName(driverClass);
二、使用DriverManager.registerDriver()注册驱动
- 前提是先创建该驱动对象
Driver driver = new OracleDriver();
DriverManager.registerDriver(driver);
三、使用系统的配置文件,添加驱动类的全路径名
String driverClass = "oracle.jdbc.driver.OracleDriver";
System.setProperty("jdbc.drivers", driverClass);
首先想到的是:是不是已经在配置文件中设置了jdbc.drivers?
System.out.println(System.getProperty("jdbc.drivers")); //结果为null
- 经过查验,发现这三种方式都没有使用
解决
- 经过反复推理,抓住了其中关键:这三种方式加载驱动,最后都是通过DriverManager类实现的
DriverManager类
- 这个类是java中管理和注册驱动的类
探究DriverManager类中源码,终于解决问题
- DriverManager中静态代码块
static {
loadInitialDrivers(); //初始化加载驱动
println("JDBC DriverManager initialized");
}
- loadInitialDrivers方法中找到第三加载驱动的方式
........
String drivers;
try {
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
return System.getProperty("jdbc.drivers"); //通过配置文件的jdbc.drivers加载驱动
}
});
} catch (Exception ex) {
drivers = null;
}
// If the driver is packaged as a Service Provider, load it.
// Get all the drivers through the classloader
// exposed as a java.sql.Driver.class service.
// ServiceLoader.load() replaces the sun.misc.Providers()
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
......
- 同样在loadInitialDrivers中找到问题的最终的原因
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class); // 通过ServiceLoader加载驱动
Iterator<Driver> driversIterator = loadedDrivers.iterator(); // 迭代查询
/* Load these drivers, so that they can be instantiated.
* It may be the case that the driver class may not be there
* i.e. there may be a packaged driver with the service class
* as implementation of java.sql.Driver but the actual class
* may be missing. In that case a java.util.ServiceConfigurationError
* will be thrown at runtime by the VM trying to locate
* and load the service.
*
* Adding a try catch block to catch those runtime errors
* if driver not available in classpath but it's
* packaged as service and that service is there in classpath.
*/
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
- ServiceLoader类
public final class ServiceLoader<S>
implements Iterable<S>
{
private static final String PREFIX = "META-INF/services/"; //PREFIX前缀
......
private LazyIterator lookupIterator; //ServiceLoader中内部类继承了迭代器接口
......
- LazyIterator中的部分源码(代码太多,有兴趣的读者可以自行查看JDK6以上的相关源码)
......
if (loader == null)
configs = ClassLoader.getSystemResources(fullName);
else
configs = loader.getResources(fullName);
......
-
最终会加载DriverManager管理下的META-INF/services/所有文件,都尝试加载
-
所以这种方式要驱动包有META-INF/services/下的包含驱动类路径的文件
注意:ojdbc5以上才有该文件