前言
ServiceLoader是實(shí)現(xiàn)SPI一個重要的類兼贡。是Mark Reinhold在java1.6引入的類乞巧,為了解決接口與實(shí)現(xiàn)分離的場景恋技。在資源目錄META-INF/services中放置提供者配置文件砂沛,文件名以接口的類名命名瞎暑,里面的內(nèi)容為需要加載的實(shí)現(xiàn)類献烦。然后在app運(yùn)行時滓窍,遇到Serviceloader.load(XxxInterface.class)時,會到META-INF/services的配置文件中尋找這個接口對應(yīng)的實(shí)現(xiàn)類全路徑名巩那,然后使用Class.forName()(傳入設(shè)定的類加載器)完成類的加載吏夯。
下面分別以JDBC的例子來講解SPI。
ServiceLoader:
在mysql-connector包中已經(jīng)滿足了spi的配置即横,現(xiàn)在看看DriverManager是如何使用ServiceLoader實(shí)現(xiàn)mysql Driver的加載的噪生。
DriverManager(jdk1.8.0_151)中有如下的靜態(tài)代碼塊:
查看loadInitialDrivers()方法,核心代碼如下:
ServiceLoader loadedDrivers = ServiceLoader.load(Driver.class);
這句代碼中將線程上下文加載器作為后續(xù)加載實(shí)現(xiàn)類的加載器(Thread.currentThread().getContextClassLoader())东囚,如果不設(shè)定的話就使用AppClassLoader作為類加載器跺嗽。并實(shí)例化ServiceLoader 和內(nèi)置的LazyIterator。
LazyIterator:實(shí)現(xiàn)懶加載機(jī)制页藻,在調(diào)用到hasNextService()和nextService()方法時才去找相應(yīng)目錄下的類名桨嫁,并加載相應(yīng)的類。
while(driversIterator.hasNext()) {
driversIterator.next();
}
在加載com.mysql.jdbc.Driver的時候Driver類有如下靜態(tài)代碼塊:
可以知道份帐,在加載Driver類的時候會new Driver()類并會注冊到DriverManager中的CopyOnWriteArrayList中璃吧。private final static CopyOnWriteArrayListregisteredDrivers =new CopyOnWriteArrayList<>();
總結(jié)
在線程創(chuàng)建過程中可以設(shè)定private ClassLoader contextClassLoader屬性,并在ServerLoader中使用該類加載器废境,使得父類加載器請求子類加載器去完成類加載的動作畜挨,打通了雙親委派模型的層次結(jié)構(gòu)來逆向使用類加載器筒繁,打破java推薦的雙親委派模型。