class.forName(org.postgresql.Driver)驅動註冊過程涉及到的原始碼中的方法
當執行class.forName(“org.postgresql.Driver”);載入驅動時執行以下方法
org.postgresql.Driver.java
public class Driver implements java.sql.Driver {
private static Driver registeredDriver;//靜態變數,再註冊方法中指向自己
private static final Logger PARENT_LOGGER = Logger.getLogger("org.postgresql");
private static final Logger LOGGER = Logger.getLogger("org.postgresql.Driver" );
private static SharedTimer sharedTimer = new SharedTimer();
static {//在類被載入時自動執行
try {
register();//執行註冊驅動方法
} catch (SQLException e) {
throw new ExceptionInInitializerError(e);
}
}
/*
*用來註冊驅動
*/
public static void register() throws SQLException {
if (isRegistered()) {//如果已經註冊過,則丟擲異常提示
throw new IllegalStateException(
"Driver is already registered. It can only be registered once.");
}
Driver registeredDriver = new Driver();//將自己例項化
DriverManager.registerDriver(registeredDriver);//把自己放入DriverManager中,進行註冊的下一步
Driver.registeredDriver = registeredDriver;//將靜態變數的引用指向自己,deregister中用到,deregister是用來登出驅動的方法
}
/*
*用來登出驅動
*/
public static void deregister() throws SQLException {
if (!isRegistered()) {
throw new IllegalStateException(
"Driver is not registered (or it has not been registered using Driver.register() method)");
}
/*registeredDriver 用在了下面*/
DriverManager.deregisterDriver(registeredDriver);
registeredDriver = null;
}
java.sql.DriverManager
“`
public class DriverManager {
/* List of registered JDBC drivers,註冊的JDBC驅動都會存放在這個容器中,CopyOnWrite是執行緒安全的,用於讀多寫少的併發場景。
CopyOnWrite容器即寫時複製的容器。通俗的理解是當我們往一個容器新增元素的時候,不直接往當前容器新增,
而是先將當前容器進行Copy,複製出一個新的容器,
然後新的容器裡新增元素,新增完元素之後,再將原容器的引用指向新的容器。
這樣做的好處是我們可以對CopyOnWrite容器進行併發的讀,而不需要加鎖,
因為當前容器不會新增任何元素。
所以CopyOnWrite容器也是一種讀寫分離的思想,讀和寫不同的容器*/。
private final static CopyOnWriteArrayList registeredDrivers = new CopyOnWriteArrayList<>();
private static volatile int loginTimeout = 0;
private static volatile java.io.PrintWriter logWriter = null;
private static volatile java.io.PrintStream logStream = null;
// Used in println() to synchronize logWriter
private final static Object logSync = new Object();
/* Prevent the DriverManager class from being instantiated. */
private DriverManager(){}
/**
* Load the initial JDBC drivers by checking the System property
* jdbc.properties and then use the {@code ServiceLoader} mechanism
*/
/*
這裡的靜態程式碼塊用來執行loadInitialDrivers方法,
用於載入配置在jdbc.drivers系統屬性內的驅動Driver,
配置在jdbc.drivers中的驅動driver將會首先被載入
*/
static {
loadInitialDrivers();
println("JDBC DriverManager initialized");
}
private static void loadInitialDrivers() {
String drivers;
try {
/*
AccessController是許可權控制器,他提供了一個預設的安全策略執行機制,
它使用棧檢查來決定潛在不安全的操作是否被允許doPrivileged是他常用的一個方法,
doPrivileged 方法能夠使一段受信任程式碼獲得更大的許可權,
甚至比呼叫它的應用程式還要多,可做到臨時訪問更多的資源,
該方法被@CallerSensitive修飾,
這個註解是JVM中專用的註解,在下面另開一塊進行記錄
*/
drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {
public String run() {
//System.getProperty()是獲取系統屬性的方法,
//DriverManager中的jdbc.drivers這個系統屬性不是本來系統自帶的,
//需要使用者自己設定採用。如果不設定,則為null。
return System.getProperty("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);
Iterator<Driver> driversIterator = loadedDrivers.iterator();
try{
while(driversIterator.hasNext()) {
driversIterator.next();
}
} catch(Throwable t) {
// Do nothing
}
return null;
}
});
println("DriverManager.initialize: jdbc.drivers = " + drivers);
if (drivers == null || drivers.equals("")) {
return;
}
String[] driversList = drivers.split(":");
println("number of Drivers:" + driversList.length);
for (String aDriver : driversList) {
try {
println("DriverManager.Initialize: loading " + aDriver);
Class.forName(aDriver, true,
ClassLoader.getSystemClassLoader());
} catch (Exception ex) {
println("DriverManager.Initialize: load failed: " + ex);
}
}
}
/*
在執行完loadInitialDrivers方法後,
由於Driver類中registerDriver方法的呼叫,會執行該方法
該方法用來註冊jdbc驅動,由於傳參不同,在此處呼叫了他的重寫方法
*/
public static synchronized void registerDriver(java.sql.Driver driver)
throws SQLException {
registerDriver(driver, null);
}
/*
這個方法比上面的方法多了一個DriverAction引數,這個DriverAction是一個介面,
也在java.sql包中,裡面只規定了一個void deregister()方法
*/
public static synchronized void registerDriver(java.sql.Driver driver,
DriverAction da)
throws SQLException {
/* Register the driver if it has not already been added to our list */
/*
執行到此驅動註冊正式完成,我們可以註冊多個驅動,
這個驅動的註冊實際就是在下面的程式碼中將當前的Driver類
放入一開始定義的靜態的CopyOnWriteArrayList容器的registeredDrivers中
放入之後,在DriverManager類中的其他方法中都可以看到從這個容器中
遍歷拿出driver使用
*/
if(driver != null) {
registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
} else {
// This is for compatibility with the original DriverManager
throw new NullPointerException();
}
println("registerDriver: " + driver);
}
“`
@CallerSensitive在類載入過過程中是可以常常看到的,他是用來找到真正發起反射請求的類的,這個註解是為了堵住漏洞用的。
用@CallerSensitive進行註解,通過此方法獲取class時會跳過鏈路上所有的有@CallerSensitive註解的方法的類,直到遇到第一個未使用該註解的類
曾經有黑客通過構造雙重反射來提升許可權,原理是當時反射只檢查固定深度的呼叫者的類,看它有沒有特權,例如固定看兩層的呼叫者(getCallerClass(2))。如果我的類本來沒足夠許可權群訪問某些資訊,那我就可以通過雙重反射去達到目的:反射相關的類是有很高許可權的,而在 我->反射1->反射2 這樣的呼叫鏈上,反射2檢查許可權時看到的是反射1的類,這就被欺騙了,導致安全漏洞。使用CallerSensitive後,getCallerClass不再用固定深度去尋找actual caller(“我”),而是把所有跟反射相關的介面方法都標註上CallerSensitive,搜尋時凡看到該註解都直接跳過,這樣就有效解決了前面舉例的問題
當”我“->反射1->反射2->反射3->反射4->…反射->N,從”我“開始後的每個引用物件,當我引用下一個物件(物件1)時,首先會被裝載,驗證,這個時候把這個物件在這個過程中沒呼叫過的反射介面都標記上@callSentitive,然後物件1引用物件2後重復這個過程一直到N,那麼在N呼叫GetCallCalss時跳過這中間所有已經被標記的物件,最後到達未被標記的“我”。