感谢阿里巴巴-陈飞给提供的帮助,然后根据自己的理解整理如下,如有错误欢迎指正,谢谢。
一、JDBC之前加载驱动的方式
在说破坏双亲委派之前,先看下之前是怎么加载Driver的。在刚开始的时候JDBC在加载class的时候,其实是直接利用了Class.classforName
//加载Oracle数据库驱动
Driver driver = (Driver)Class.forName("oracle.jdbc.driver.OracleDriver");
//加载SQL Server数据库驱动
Driver driver = (Driver)Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
//加载MySQL 数据库驱动
Driver driver = (Driver)Class.forName("com.mysql.jdbc.Driver");
Class.forName()将对应的驱动类加载到内存中,然后执行内存中的static静态代码段,代码段中,会创建一个驱动Driver的实例,放入DriverManager中,供DriverManager使用。
static {
Timestamp localTimestamp = Timestamp.valueOf("2000-01-01 00:00:00.0");
try {
if (defaultDriver == null) {
//创建一个OracleDriver实例,然后注册到DriverManager中
defaultDriver = new OracleDriver();
DriverManager.registerDriver(defaultDriver);
}
} catch (RuntimeException localRuntimeException) {
} catch (SQLException localSQLException) {
}
下面是一个加载Driver、注册、注销、重新注册的代码示例:
public static void defaultDriver(){
try {
String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";
//1.将Driver加载到内存中,然后执行其static静态代码,创建一个OracleDriver实例注册到DriverManager中
Driver dd = (Driver)Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
//2.取出对应的oracle 驱动Driver
Driver driver = DriverManager.getDriver(url);
System.out.println("加载类后,获取Driver对象:"+driver);
//3. 将driver从DriverManager中注销掉
DriverManager.deregisterDriver(driver);
//4.此时DriverManager中已经没有了驱动Driver实例,将创建的dd注册到DriverManager中
DriverManager.registerDriver(dd);
//5.重新通过url从DriverManager中取Driver
driver = DriverManager.getDriver(url);
System.out.println("注销掉静态创建的Driver后,重新注册的Driver: "+driver);
System.out.println("driver和dd是否是同一对象:" +(driver==dd));
} catch (Exception e) {
System.out.println("加载Oracle类失败!");
e.printStackTrace();
} finally{
}
}
以上其实就是早期我们加载JDBC时候的方式。但是这种从上面的代码上也可以看出,假如说我要加载不同的Driver,那么我要去加载不同的url,来实例化不同的Driver,这样的话会造成代码的冗余,或者说代码之间的耦合性太强(代码要使用的Driver和和Driver的实现耦合到一起),还有就是如上面写的类于“oracle.jdbc.driver.OracleDriver”这样的一堆的字符串,所以,之后就出现了利用破坏双亲委派这种机制来加载Driver的方式。
二、JDBC现在加载驱动的方式
接下来看java是如何破坏双亲委派来加载 Driver的,看源码,首先要一个入口:
public static void main(String[] args) throws SQLException {
String url = "jdbc:mysql://118.190.159.10:3306/test?characterEncoding=utf-8&useSSL=false";
String username = "root";
String password = "Yasong0120.";
Connection conn = null;
try {
conn = DriverManager.getConnection(url, username, password);
PreparedStatement statement = conn.prepareStatement("select * from user where id = ?");
statement.setLong(1,1);
ResultSet set = statement.executeQuery();
while(set.next()){
System.out.println(set.getString("name"));
}
conn.close();
} catch (SQLException e) {
e.printStackTrace();
} finally {
if(conn!=null){
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
}
代码在执行DriverManager.getConnection(url, username, password); 首先回去实例化DriverManager,而DriverManager是整个加载的核心
DriverManager里面有一个静态的代码块,在实例化的时候会进行初始化执行。
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
loadInitialDrivers();整个方法里面最核心也是执行加载的核心的两行代码如下:
ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
Iterator driversIterator = loadedDrivers.iterator();
下面的load方法重点在于ClassLoader cl = Thread.currentThread().getContextClassLoader();
线程上下文件类加载器(Thread Context ClassLoader)。这个类加载器可以通过java.lang.Thread类的setContextClassLoader()方法进行设置,如果创建线程时还未设置,它将会从父线程中继承一个;如果在应用程序的全局范围内都没有设置过,那么这个类加载器默认就是应用程序类加载器。
public static ServiceLoader load(Class service) {
//因为传入的service 是 java.sql.Driver,它是一个接口,jdk支持了 spi 这种提供接口的方式
//供调用者去实现它的接口,具体调用者去调用哪个实现,Driver不管。
//因为当前service 是一个第三方的类或者说非java自己的类,所以JVM 是无法调用自己的
//类加载器去加载这个Driver类的,所以它需要一个appClassLoader去加载,所以它用了
//TCCL去获取一个 aapClassLoader,然后去执行实例化。
// spi 指的是(service provide interface),第三方可以以jar 的形式提供一个接口类,
//调用者 可以根据自己的业务去实现。
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
下面是具体的实例化的代码,它的底层其实是用了Class.forName去进行实例化。至此,java在加载Driver的时候是怎么去破坏双亲模型的给说完了,剩下的代码基本上不属于破坏双亲模型的范畴。
private S nextService() {
if (!hasNextService())
throw new NoSuchElementException();
String cn = nextName;
nextName = null;
Class> c = null;
try {
c = Class.forName(cn, false, loader);
} catch (ClassNotFoundException x) {
fail(service,
"Provider " + cn + " not found");
}
if (!service.isAssignableFrom(c)) {
fail(service,
"Provider " + cn + " not a subtype");
}
try {
S p = service.cast(c.newInstance());
providers.put(cn, p);
return p;
} catch (Throwable x) {
fail(service,
"Provider " + cn + " could not be instantiated",
x);
}
throw new Error(); // This cannot happen
}