Java中的SPI源碼及DriverManager分析

SPI相關(guān)源碼分析

  • ClassLoader
//F7單步執(zhí)行不會(huì)進(jìn)入,必須使用alt+shift+F7強(qiáng)制進(jìn)入
//AppClassLoader
loader.getResources(fullName);

//ExtClassLoader圆存、AppClassLoader都繼承UrlClassLoader再繼承ClassLoader店归,UrlClassLoader中包含UrlClassPath ucp字段,
//返回一個(gè)枚舉器(懶惰加載)醒叁,遍歷枚舉器時(shí)才會(huì)
Enumeration<Resource> e = UrlClassPath.getResources(String className)
//根據(jù)className遍歷loaders進(jìn)行加載   
e.hasMoreElement()或e.next()

public Enumeration<URL> getResources(String name) throws IOException {
    Enumeration<URL>[] tmp = (Enumeration<URL>[]) new Enumeration<?>[2];
    if (parent != null) {
        // AppClassLoader的parent為ExtClassLoader司浪,獲取其組合的枚舉器
        tmp[0] = parent.getResources(name);
    } else {
        // ExtClassLoader的parent為null泊业,組合BootstrapResources
        tmp[0] = getBootstrapResources(name);
    }
    tmp[1] = findResources(name);
    return new CompoundEnumeration<>(tmp);
}
  • CompoundEnumeration
private boolean next() {
//遍歷枚舉器數(shù)組找到滿足情況的枚舉
while(this.index < this.enums.length) {
    if (this.enums[this.index] != null && this.enums[this.index].hasMoreElements()) {
        return true;
    }
    ++this.index;
}
return false;   
}
  • UrlClassPath
public Enumeration<Resource> getResources(final String var1, final boolean var2) {
return new Enumeration<Resource>() {
private int index = 0;
private int[] cache = URLClassPath.this.getLookupCache(var1);
private Resource res = null;

private boolean next() {
    if (this.res != null) {
        return true;
    } else {
        do {
            URLClassPath.Loader var1x;
            // UrlClassPath中包含ArrayList<URLClassPath.Loader> loaders字段,每個(gè)loader一般情況下是一個(gè)jarLoader,負(fù)責(zé)加載jar文件
            if ((var1x = URLClassPath.this.getNextLoader(this.cache, this.index++)) == null) {
                return false;
            }
            // 遍歷jarLoader啊易,確定其是否找到對(duì)應(yīng)的class
            this.res = var1x.getResource(var1, var2);
        } while(this.res == null);

        return true;
    }
}
  • ServiceLoader
// ServiceLoader.LazyIterator
// 判斷迭代器中是否還有下一個(gè)服務(wù)
private boolean hasNextService() {
    if (nextName != null) {
        return true;
    }
    if (configs == null) {
        // 組裝META-INF\services\className
        String fullName = PREFIX + service.getName();
        if (loader == null)
            configs = ClassLoader.getSystemResources(fullName);
        else
        // 使用AppClassLoader生成枚舉器
            configs = loader.getResources(fullName);
    }
    while ((pending == null) || !pending.hasNext()) {
        //第一次進(jìn)入或當(dāng)?shù)谝粋€(gè)META-INF\services\className的file中的所有實(shí)現(xiàn)類已經(jīng)初始化完成后吁伺,繼續(xù)找其他的實(shí)現(xiàn)類file
        if (!configs.hasMoreElements()) {
            return false;
        }
        // 解析找到的包含了META-INF\services\className的file的URL,得到實(shí)現(xiàn)類名稱的迭代器
        pending = parse(service, configs.nextElement());
    }
    // 返回實(shí)現(xiàn)類名稱
    nextName = pending.next();
    return true;
}

private S nextService() {
    if (!hasNextService())
        throw new NoSuchElementException();
    String cn = nextName;
    // 設(shè)置為null租谈,便于下一次hashNextService時(shí)得到下一個(gè)實(shí)現(xiàn)類的名稱
    nextName = null;
    Class<?> c = null;
    // 找到實(shí)現(xiàn)類Class
    //第二個(gè)參數(shù)表示是否執(zhí)行該class的初始化篮奄,初始化的概念如下
    https://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.4
    Initialization of a class consists of executing its static initializers and the initializers for static fields (class variables) declared in the class.
    Initialization of an interface consists of executing the initializers for fields (constants) declared in the interface.
    c = Class.forName(cn, false, loader);
    S p = service.cast(c.newInstance());
    //放入providers數(shù)組中
    providers.put(cn, p);
    return p;
}
  • DriverManager實(shí)踐
static {
    loadInitialDrivers();
    println("JDBC DriverManager initialized");
}
    
private static void loadInitialDrivers() {
   // 
   ServiceLoader<Driver> loadedDrivers = ServiceLoader.load(Driver.class);
   Iterator<Driver> driversIterator = loadedDrivers.iterator();
   //找到下一個(gè)實(shí)現(xiàn)類
   while(driversIterator.hasNext()) {
      //初始化下一個(gè)實(shí)現(xiàn)類并放入ServiceLoader.providers中
      driversIterator.next();
   }
   //第一次遍歷迭代器從lazyIterator中獲取對(duì)象并設(shè)置到providers中,再次初始化迭代器直接從providers中獲取對(duì)應(yīng)的實(shí)現(xiàn)類
   //driversIterator = loadedDrivers.iterator();
}

//早期的jdbc使用方式
//注冊(cè) JDBC 驅(qū)動(dòng)
Class.forName(JDBC_DRIVER);
//不確定是如何使用注冊(cè)的Driver的
conn = DriverManager.getConnection(DB_URL,USER,PASS);

//后續(xù)的方式
//com.mysql.jdbc.Driver&org.postgresql.Driver都包含如下內(nèi)容
static {
//注冊(cè)自己的Driver到DriverManager.registeredDrivers(static)中
//由ServiceLoader中S p = service.cast(c.newInstance()),c為Driver對(duì)應(yīng)的Class割去,觸發(fā)行為
DriverManager.registerDriver(new Driver());
}
            
//DriverManager.getConnection()
//
for(DriverInfo aDriver : registeredDrivers) {
    //測試使用postgrel的Driver去連mysql窟却,此處返回null
    Connection con = aDriver.driver.connect(url, info);
    if (con != null) {
        // Success!
        println("getConnection returning " + aDriver.driver.getClass().getName());
        return (con);
    }
}
  • 不足

接口所有的實(shí)現(xiàn)類都newInstance()
ServiceLoader對(duì)多線程不夠友好

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市呻逆,隨后出現(xiàn)的幾起案子夸赫,更是在濱河造成了極大的恐慌,老刑警劉巖咖城,帶你破解...
    沈念sama閱讀 216,843評(píng)論 6 502
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件茬腿,死亡現(xiàn)場離奇詭異,居然都是意外死亡宜雀,警方通過查閱死者的電腦和手機(jī)滓彰,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,538評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來州袒,“玉大人揭绑,你說我怎么就攤上這事±煽蓿” “怎么了他匪?”我有些...
    開封第一講書人閱讀 163,187評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長夸研。 經(jīng)常有香客問我邦蜜,道長,這世上最難降的妖魔是什么亥至? 我笑而不...
    開封第一講書人閱讀 58,264評(píng)論 1 292
  • 正文 為了忘掉前任悼沈,我火速辦了婚禮,結(jié)果婚禮上姐扮,老公的妹妹穿的比我還像新娘絮供。我一直安慰自己,他們只是感情好茶敏,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,289評(píng)論 6 390
  • 文/花漫 我一把揭開白布壤靶。 她就那樣靜靜地躺著,像睡著了一般惊搏。 火紅的嫁衣襯著肌膚如雪贮乳。 梳的紋絲不亂的頭發(fā)上忧换,一...
    開封第一講書人閱讀 51,231評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音向拆,去河邊找鬼亚茬。 笑死,一個(gè)胖子當(dāng)著我的面吹牛浓恳,可吹牛的內(nèi)容都是我干的刹缝。 我是一名探鬼主播,決...
    沈念sama閱讀 40,116評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼奖蔓,長吁一口氣:“原來是場噩夢啊……” “哼赞草!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起吆鹤,我...
    開封第一講書人閱讀 38,945評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤厨疙,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后疑务,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沾凄,經(jīng)...
    沈念sama閱讀 45,367評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,581評(píng)論 2 333
  • 正文 我和宋清朗相戀三年知允,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了撒蟀。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,754評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡温鸽,死狀恐怖保屯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情涤垫,我是刑警寧澤姑尺,帶...
    沈念sama閱讀 35,458評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站蝠猬,受9級(jí)特大地震影響切蟋,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜榆芦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,068評(píng)論 3 327
  • 文/蒙蒙 一柄粹、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧匆绣,春花似錦驻右、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,692評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至凯力,卻和暖如春茵瘾,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背咐鹤。 一陣腳步聲響...
    開封第一講書人閱讀 32,842評(píng)論 1 269
  • 我被黑心中介騙來泰國打工拗秘, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人祈惶。 一個(gè)月前我還...
    沈念sama閱讀 47,797評(píng)論 2 369
  • 正文 我出身青樓雕旨,卻偏偏與公主長得像,于是被迫代替她去往敵國和親捧请。 傳聞我的和親對(duì)象是個(gè)殘疾皇子凡涩,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,654評(píng)論 2 354