一暴氏、JDBC之前加載驅(qū)動(dòng)的方式
在說(shuō)破壞雙親委派之前,先看下之前是怎么加載Driver的绣张。在剛開(kāi)始的時(shí)候JDBC在加載class的時(shí)候答渔,其實(shí)是直接利用了Class.classforName
Class.forName()和ClassLoader.loadClass區(qū)別
Class.forName(className)方法,內(nèi)部實(shí)際調(diào)用的方法是 Class.forName(className,true,classloader);
第2個(gè)boolean參數(shù)表示類是否需要初始化侥涵, Class.forName(className)默認(rèn)是需要初始化沼撕。
一旦初始化,就會(huì)觸發(fā)目標(biāo)對(duì)象的 static塊代碼執(zhí)行芜飘,static參數(shù)也也會(huì)被再次初始化端朵。
ClassLoader.loadClass(className)方法,內(nèi)部實(shí)際調(diào)用的方法是 ClassLoader.loadClass(className,false);
第2個(gè) boolean參數(shù)燃箭,表示目標(biāo)對(duì)象是否進(jìn)行鏈接冲呢,false表示不進(jìn)行鏈接,由上面介紹可以招狸,
不進(jìn)行鏈接意味著不進(jìn)行包括初始化等一些列步驟敬拓,那么靜態(tài)塊和靜態(tài)對(duì)象就不會(huì)得到執(zhí)行
//加載Oracle數(shù)據(jù)庫(kù)驅(qū)動(dòng)
Driver driver = (Driver)Class.forName("oracle.jdbc.driver.OracleDriver");
//加載SQL Server數(shù)據(jù)庫(kù)驅(qū)動(dòng)
Driver driver = (Driver)Class.forName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
//加載MySQL 數(shù)據(jù)庫(kù)驅(qū)動(dòng)
Driver driver = (Driver)Class.forName("com.mysql.jdbc.Driver");
Class.forName()將對(duì)應(yīng)的驅(qū)動(dòng)類加載到內(nèi)存中,然后執(zhí)行內(nèi)存中的static靜態(tài)代碼段裙戏,代碼段中乘凸,會(huì)創(chuàng)建一個(gè)驅(qū)動(dòng)Driver的實(shí)例,放入DriverManager中累榜,供DriverManager使用营勤。
static {
Timestamp localTimestamp = Timestamp.valueOf("2000-01-01 00:00:00.0");
try {
if (defaultDriver == null) {
//創(chuàng)建一個(gè)OracleDriver實(shí)例,然后注冊(cè)到DriverManager中
defaultDriver = new OracleDriver();
DriverManager.registerDriver(defaultDriver);
}
} catch (RuntimeException localRuntimeException) {
} catch (SQLException localSQLException) {
}
下面是一個(gè)加載Driver壹罚、注冊(cè)葛作、注銷、重新注冊(cè)的代碼示例:
public static void defaultDriver(){
try {
String url = "jdbc:oracle:thin:@127.0.0.1:1521:xe";
//1.將Driver加載到內(nèi)存中猖凛,然后執(zhí)行其static靜態(tài)代碼赂蠢,創(chuàng)建一個(gè)OracleDriver實(shí)例注冊(cè)到DriverManager中
Driver dd = (Driver)Class.forName("oracle.jdbc.driver.OracleDriver").newInstance();
//2.取出對(duì)應(yīng)的oracle 驅(qū)動(dòng)Driver
Driver driver = DriverManager.getDriver(url);
System.out.println("加載類后,獲取Driver對(duì)象:"+driver);
//3. 將driver從DriverManager中注銷掉
DriverManager.deregisterDriver(driver);
//4.此時(shí)DriverManager中已經(jīng)沒(méi)有了驅(qū)動(dòng)Driver實(shí)例辨泳,將創(chuàng)建的dd注冊(cè)到DriverManager中
DriverManager.registerDriver(dd);
//5.重新通過(guò)url從DriverManager中取Driver
driver = DriverManager.getDriver(url);
System.out.println("注銷掉靜態(tài)創(chuàng)建的Driver后虱岂,重新注冊(cè)的Driver: "+driver);
System.out.println("driver和dd是否是同一對(duì)象:" +(driver==dd));
} catch (Exception e) {
System.out.println("加載Oracle類失敗菠红!");
e.printStackTrace();
} finally{
}
}
以上其實(shí)就是早期我們加載JDBC時(shí)候的方式第岖。但是這種從上面的代碼上也可以看出,假如說(shuō)我要加載不同的Driver试溯,那么我要去加載不同的
url,來(lái)實(shí)例化不同的Driver,這樣的話會(huì)造成代碼的冗余蔑滓,或者說(shuō)代碼之間的耦合性太強(qiáng)(代碼要使用的Driver和和Driver的實(shí)現(xiàn)耦合到一起),
還有就是如上面寫(xiě)的類似于“oracle.jdbc.driver.OracleDriver”這樣的一堆的字符串,所以烫饼,之后就出現(xiàn)了利用破壞雙親委派這種機(jī)制來(lái)加載Driver的方式。
二试读、JDBC現(xiàn)在加載驅(qū)動(dòng)的方式
接下來(lái)看java是如何破壞雙親委派來(lái)加載 Driver的杠纵,看源碼,首先要一個(gè)入口:
public static void main(String[] args) throws SQLException {
String url = "jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&useSSL=false";
String username = "root";
String password = "Ys0120.";
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();
}
}
}
}
代碼在執(zhí)行DriverManager.getConnection(url, username, password); 首先回去實(shí)例化DriverManager钩骇,而DriverManager是整個(gè)加載的核心
DriverManager里面有一個(gè)靜態(tài)的代碼塊,在實(shí)例化的時(shí)候會(huì)進(jìn)行初始化執(zhí)行比藻。
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
loadInitialDrivers();整個(gè)方法里面最核心也是執(zhí)行加載的核心的兩行代碼如下:
ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
下面的load方法重點(diǎn)在于ClassLoader cl = Thread.currentThread().getContextClassLoader();
線程上下文件類加載器(Thread Context ClassLoader)。這個(gè)類加載器可以通過(guò)java.lang.Thread類的setContextClassLoader()方法進(jìn)行設(shè)置倘屹,如果創(chuàng)建線程時(shí)還未設(shè)置银亲,它將會(huì)從父線程中繼承一個(gè);如果在應(yīng)用程序的全局范圍內(nèi)都沒(méi)有設(shè)置過(guò)纽匙,那么這個(gè)類加載器默認(rèn)就是應(yīng)用程序類加載器务蝠。
public static <S> ServiceLoader<S> load(Class<S> service) {
//因?yàn)槌鋈氲膕ervice 是 java.sql.Driver,它是一個(gè)接口烛缔,jdk支持了 spi 這種提供接口的方式
//供調(diào)用者去實(shí)現(xiàn)它的接口馏段,具體調(diào)用者去調(diào)用哪個(gè)實(shí)現(xiàn),Driver不管践瓷。
//因?yàn)楫?dāng)前service 是一個(gè)第三方的類或者說(shuō)非java自己的類院喜,所以JVM 是無(wú)法調(diào)用自己的
//類加載器去加載這個(gè)Driver類的,所以它需要一個(gè)appClassLoader去加載晕翠,所以它用了
//TCCL去獲取一個(gè) aapClassLoader喷舀,然后去執(zhí)行實(shí)例化。
// spi 指的是(service provide interface)淋肾,第三方可以以jar 的形式提供一個(gè)接口類硫麻,
//調(diào)用者 可以根據(jù)自己的業(yè)務(wù)去實(shí)現(xiàn)。
ClassLoader cl = Thread.currentThread().getContextClassLoader();
return ServiceLoader.load(service, cl);
}
下面是具體的實(shí)例化的代碼樊卓,它的底層其實(shí)是用了Class.forName去進(jìn)行實(shí)例化庶香。至此,java在加載Driver
的時(shí)候是怎么去破壞雙親模型的給說(shuō)完了简识,剩下的代碼基本上不屬于破壞雙親模型的范疇赶掖。
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
}