类加载器-线程上下文
線程上下文類加載器
我們在使用 JDBC 時(shí),都需要加載 Driver 驅(qū)動,不知道你注意到?jīng)]有,不寫
Class.forName("com.mysql.jdbc.Driver")也是可以讓 com.mysql.jdbc.Driver 正確加載的,你知道是怎么做的嗎?
讓我們追蹤一下源碼:
public class DriverManager {// 注冊驅(qū)動的集合private final static CopyOnWriteArrayList<DriverInfo> registeredDrivers= new CopyOnWriteArrayList<>();// 初始化驅(qū)動static {loadInitialDrivers();println("JDBC DriverManager initialized");} }先不看別的,看看 DriverManager 的類加載器:
System.out.println(DriverManager.class.getClassLoader());打印 null,表示它的類加載器是 Bootstrap ClassLoader,會到 JAVA_HOME/jre/lib 下搜索類,但JAVA_HOME/jre/lib 下顯然沒有 mysql-connector-java-5.1.47.jar 包,這樣問題來了,在DriverManager 的靜態(tài)代碼塊中,怎么能正確加載 com.mysql.jdbc.Driver 呢?
繼續(xù)看 loadInitialDrivers() 方法:
private static void loadInitialDrivers() {String drivers;try {drivers = AccessController.doPrivileged(new PrivilegedAction<String>() {public String run() {return System.getProperty("jdbc.drivers");}});} catch (Exception ex) {drivers = null;}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); // 2)使用 jdbc.drivers 定義的驅(qū)動名加載驅(qū)動 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);// 這里的 ClassLoader.getSystemClassLoader() 就是應(yīng)用程序類加載器Class.forName(aDriver, true,ClassLoader.getSystemClassLoader());} catch (Exception ex) {println("DriverManager.Initialize: load failed: " + ex);} }先看 2)發(fā)現(xiàn)它最后是使用 Class.forName 完成類的加載和初始化,關(guān)聯(lián)的是應(yīng)用程序類加載器,因此可以順利完成類加載
再看 1)它就是大名鼎鼎的 Service Provider Interface (SPI)
約定如下,在 jar 包的 META-INF/services 包下,以接口全限定名名為文件,文件內(nèi)容是實(shí)現(xiàn)類名稱
這樣就可以使用
ServiceLoader<接口類型> allImpls = ServiceLoader.load(接口類型.class); Iterator<接口類型> iter = allImpls.iterator(); while(iter.hasNext()) {iter.next(); }來得到實(shí)現(xiàn)類,體現(xiàn)的是【面向接口編程+解耦】的思想,在下面一些框架中都運(yùn)用了此思想:
?? ?JDBC
?? ?Servlet 初始化器
?? ?Spring 容器
?? ?Dubbo(對 SPI 進(jìn)行了擴(kuò)展)
接著看 ServiceLoader.load 方法:
public static <S> ServiceLoader<S> load(Class<S> service) {// 獲取線程上下文類加載器ClassLoader cl = Thread.currentThread().getContextClassLoader();return ServiceLoader.load(service, cl); }線程上下文類加載器是當(dāng)前線程使用的類加載器,默認(rèn)就是應(yīng)用程序類加載器,它內(nèi)部又是由Class.forName 調(diào)用了線程上下文類加載器完成類加載,具體代碼在 ServiceLoader 的內(nèi)部類LazyIterator 中:
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,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} }?
總結(jié)
以上是生活随笔為你收集整理的类加载器-线程上下文的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 类加载器-双亲委派-源码分析2
- 下一篇: 类加载器-自定义