JDBC如何破坏双亲委派机制
JDBC的注冊會涉及到java spi機制,即Service Provideer Interface,主要應用于廠商自定義組件或插件中;簡單說就是java來定義接口規(guī)則和方法,廠商實現(xiàn)具體邏輯,每家廠商根據(jù)自己產(chǎn)品實現(xiàn)的邏輯肯定不相同,但上層直接使用接口時感覺不到取別。就比如java.sql.Dirver。
java spi的具體約定:廠商在自己被引用的jar包下的META-INF/services目錄下創(chuàng)建一個以服務接口命名的文件,然后指向具體實現(xiàn)類。
在裝載的時候,ServiceLoader這個類就會掃描對應目錄,找到這個實現(xiàn)類
但是無論是Driver還是ServiceLoader都在java核心庫rt中,他們可以用啟動類加載器進行加載;而根據(jù)雙親委派機制,啟動類加載器是加載不了實現(xiàn)類的,因為不在rt庫中,那么這里就要想別的辦法加載了。
還好Launcher這個創(chuàng)建擴展類加載器和系統(tǒng)類加載器的類是在rt中,那么也就是說我們可以用Java.lang.Thread中的方法 getContextClassLoader()和 setContextClassLoader(ClassLoader cl)來獲取和設置線程的上下文類加載器。
?
public Launcher() {Launcher.ExtClassLoader var1;try {var1 = Launcher.ExtClassLoader.getExtClassLoader();} catch (IOException var10) {throw new InternalError("Could not create extension class loader", var10);}try {this.loader = Launcher.AppClassLoader.getAppClassLoader(var1);} catch (IOException var9) {throw new InternalError("Could not create application class loader", var9);}//此處可以看到初始的線程類加載器就是系統(tǒng)類加載器Thread.currentThread().setContextClassLoader(this.loader);String var2 = System.getProperty("java.security.manager");if (var2 != null) {SecurityManager var3 = null;if (!"".equals(var2) && !"default".equals(var2)) {try {var3 = (SecurityManager)this.loader.loadClass(var2).newInstance();} catch (IllegalAccessException var5) {} catch (InstantiationException var6) {} catch (ClassNotFoundException var7) {} catch (ClassCastException var8) {}} else {var3 = new SecurityManager();}if (var3 == null) {throw new InternalError("Could not create SecurityManager: " + var2);}System.setSecurityManager(var3);}}思路是這樣的,那么具體的加載過程如何呢
在JDBC規(guī)范中明確要求Drive類必須向DriverManager注冊自己,所以是用Class.forName也好,設置System.setProperty也好,都是要過DriverManager。
static {loadInitialDrivers();println("JDBC DriverManager initialized"); }在類初始化執(zhí)行clinit方法的時候,會執(zhí)行對應的靜態(tài)代碼塊,也可以說這是一個類最先被執(zhí)行的代碼,里面有個loadInitialDrivers方法
private static void loadInitialDrivers() {String drivers;//一上來先看看有沒有通過System.setProperty配置實現(xiàn)類try {drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {public String run() {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()//這里就是另一種用ServiceLoader掃描的方式加載實現(xiàn)類,下面展開說AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);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;}});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 {//drivers都掃描到了,然后就挨個加載類唄,這里用的是Class.forName的方式,使用SystemClassLoader,默認是系統(tǒng)類加載器println("DriverManager.Initialize: loading " + aDriver);Class.forName(aDriver, true,ClassLoader.getSystemClassLoader());} catch (Exception ex) {println("DriverManager.Initialize: load failed: " + ex);}} }上面代碼使用ServiceLoader的部分實際上是完整的加載流程,如果這段被執(zhí)行,說明drivers為空直接return。然后這部分代碼展開細說一下。
public static <S> ServiceLoader<S> load(Class<S> service) {ClassLoader cl = Thread.currentThread().getContextClassLoader();return ServiceLoader.load(service, cl); }public static <S> ServiceLoader<S> load(Class<S> service,ClassLoader loader){return new ServiceLoader<>(service, loader);}private ServiceLoader(Class<S> svc, ClassLoader cl) {service = Objects.requireNonNull(svc, "Service interface cannot be null");loader = (cl == null) ? ClassLoader.getSystemClassLoader() : cl;acc = (System.getSecurityManager() != null) ? AccessController.getContext() : null;reload();}public void reload() {providers.clear();lookupIterator = new LazyIterator(service, loader);}ServiceLoader里面一頓操作,可以確定的是使用的ClassLoader要么取自線程,要么取自system,反正是系統(tǒng)類及系統(tǒng)類加載器之下的類加載器。然后這里有個reload方法,里面搞了個LazyIterator,兩個參數(shù),一個spi接口,一個加載器,要干啥不言而喻。
private class LazyIteratorimplements Iterator<S> {Class<S> service;ClassLoader loader;Enumeration<URL> configs = null;Iterator<String> pending = null;String nextName = null;private LazyIterator(Class<S> service, ClassLoader loader) {this.service = service;this.loader = loader;}private boolean hasNextService() {if (nextName != null) {return true;}if (configs == null) {try {String fullName = PREFIX + service.getName();if (loader == null)configs = ClassLoader.getSystemResources(fullName);elseconfigs = loader.getResources(fullName);} catch (IOException x) {fail(service, "Error locating configuration files", x);}}while ((pending == null) || !pending.hasNext()) {if (!configs.hasMoreElements()) {return false;}pending = parse(service, configs.nextElement());}nextName = pending.next();return true;}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}public boolean hasNext() {if (acc == null) {return hasNextService();} else {PrivilegedAction<Boolean> action = new PrivilegedAction<Boolean>() {public Boolean run() { return hasNextService(); }};return AccessController.doPrivileged(action, acc);}}public S next() {if (acc == null) {return nextService();} else {PrivilegedAction<S> action = new PrivilegedAction<S>() {public S run() { return nextService(); }};return AccessController.doPrivileged(action, acc);}}public void remove() {throw new UnsupportedOperationException();}}很明顯LazyIterator是重寫了Iterator的hasNext和next方法,然后同樣使用Class.forName加載實現(xiàn)類,所以DriverManager里面直接調(diào)用next就能加載driver實現(xiàn)類。
SPI: 在Java平臺中,通常把核心類rt.jar中提供外部服務、可由應用層自行實現(xiàn)的接口稱為SPI。
總結
以上是生活随笔為你收集整理的JDBC如何破坏双亲委派机制的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Mybatis_Plus日常开发遇到问题
- 下一篇: multisim收敛助手有啥用_Mult