JavaのSPIメカニズムの深い理解

この記事では、最初のインターネット技術の生体内マイクロチャネル公共番号に登場し 
たリンク:https://mp.weixin.qq.com/s/vpy5DJ-hhn0iOyp747oL5A
著者:江列

SPI(サービスプロバイダインタフェース)は、ビルトインされたJDKサービス発見メカニズムを提供し、この記事では、Java SPIメカニズムへの漸進的なアプローチを説明します。

I.はじめに

SPI(サービスプロバイダインタフェース)は、JDKが構築されているサービスプロバイダディスカバリメカニズムは、主にこのようなに、java.sql.driverインターフェースとしてフレームワークを使用して開発者が、拡張し、コンポーネントを交換するためのフレームワークを有効にするために使用することができ、様々な他のベンダーは、標的とすることができます異なる実装を作るために、同じインターフェイスインターフェース機構としてSPI Javaはサービス実装を探してもよいが、MySQLとPostgreSQLは、ユーザに利用可能な異なる実装を有します。Javaは、SPI機構主なアイデアは、この機構は、モジュラー設計において特に重要であり、コアアイデアがである、組立工程の制御を越えて移動することで切り離します

SPIとAPIの違い:

  • APIコールと目標を達成するために、クラス、インタフェース、メソッドなどを記述するために使用されます。

  • SPIは、拡張し、ターゲットクラス、インタフェース、メソッドを達成するために実装され、等記載されています。

言い換えれば、APIは特定のクラス、メソッドを満たして動作させることにより、特定の操作のためのクラス、メソッド、SPIを提供しています。

参考:https://stackoverflow.com/questions/2954372/difference-between-spi-and-api?answertab=votes#tab-top

次のようにSPI全体のメカニズムは次のとおりです。

プロバイダのサービスは後に、あなたはクラスパス/サービス/ディレクトリの下にMETA-INFという名前のファイルにサービス・インターフェースを作成する必要があるインターフェイスを実装する場合、このファイルには、このインタフェースの特定のカテゴリのコンテンツです。他のプログラムがこのサービスを必要としますが、あなたはMETA-INF /サービス/コンフィギュレーションファイルのこのjarパッケージ(通常に依存するJARパッケージを実行するには)、コンフィギュレーション・ファイル名・インタフェースの実装クラスを見つけることができますクラス名をインスタンス化するに従って、ロードすることができ、あなたがサービスを利用することができます。java.util.ServiceLoader:で実現するサービスのJDKツールを検索します。

第二に、アプリケーションのシナリオ

SPI拡張メカニズムは、このような共通のロギングなど多くのシナリオがある、JDBC、ダボが好きです。

SPIプロセス:

  1. 式で定義される関係機関とのインターフェース規格

  2. サードパーティが具現化:具体的な方法を達成するために、構成のMETA-INF /サービス/ $ {interface_nameに}ファイル

  3. 開発者の使用

例えば、JDBC次のシーン:

  • 最初のJavaインターフェースに、java.sql.driverで定義され、特定の実装の具体的な実装は、異なるベンダーによって提供されていません。

  • MySQLでは、JARパッケージのmysql-コネクタのjava-6.0.6.jarは、META-INF / servicesディレクトリ見つけることができ、ディレクトリ内に、java.sql.driverファイルの名前があるだろう、ファイルの内容がcom.mysqlです。 cj.jdbc.Driverは、コンテンツは、Java用に定義されたインタフェースの実装であります。

  • これは、JavaのPostgreSQLのに、java.sql.driverの実現である、org.postgresql.Driverであるとしても、瓶の中のPostgreSQL PostgreSQLの-42.0.0.jarも、同じコンフィギュレーション・ファイルを見つけることができます。

第三に、使用のデモ

1.インタフェースHelloSPIを定義します。

パッケージcom.vivo.study.spidemo.spi。
パブリックインターフェースHelloSPI { 
    ボイドのsayHello(); 
}

前記複数の実装のインタフェースを完了。

パッケージcom.vivo.study.spidemo.spi.impl。
輸入com.vivo.study.spidemo.spi.HelloSPI。
パブリッククラスImageHelloはHelloSPI {実装し
    ます。public voidのsayHello(){ 
        System.out.printlnは( "イメージこんにちは"); 
    } 
}

 

パッケージcom.vivo.study.spidemo.spi.impl。
輸入com.vivo.study.spidemo.spi.HelloSPI。
パブリッククラスTextHelloはHelloSPI {実装し
    ます。public voidのsayHello(){ 
        System.out.printlnは( "テキストこんにちは"); 
    } 
}

ファイルを作成するCom.vivo.study.spidemo.spi.HelloSPI、このファイルには、META-INF /サービス/ディレクトリにこのインタフェースの特定のカテゴリのコンテンツです。

詳細は以下のとおりです。

com.vivo.study.spidemo.spi.impl.ImageHello 
com.vivo.study.spidemo.spi.impl.TextHello

 

3. ServiceLoader設定ファイルで指定された負荷を達成するために

com.vivo.study.spidemo.testパッケージ
インポートjava.util.ServiceLoader; 
インポートcom.vivo.study.spidemo.spi.HelloSPI; 
publicクラスSPIDemo { 
    パブリック静的無効メイン(文字列[] args){ 
        ServiceLoader <HelloSPI> ServiceLoader ServiceLoader.load =(HelloSPI.class); 
        //は、異なるベンダーのサービスの実装を行う、具体的な構成のサービスニーズに応じ
        ため(helloSPI helloSPI:ServiceLoader){ 
            helloSPI.sayHello(); 
        } 
    } 
}

出力:

画像ハロー
テキストこんにちは

第四に、ソースコード解析

// ServiceLoaderは、反復処理可能なインターフェイスを実装し、サービスの実装のすべての横断できる
パブリックServiceLoader finalクラス<S>道具のIterable <S>を
{ 
    //設定ファイルを見つける
    プライベート静的最終文字列のPREFIX = "META -INF /サービス/"; 
    //サービスクラスまたはインタフェースにロードされると言わ
    プライベートクラス決勝は、<S> -service; 
    //、負荷を配置するために使用されるこのクラスローダは、サービスプロバイダインスタンス
    民間最終クラスローダーローダーを; 
    //アクセス制御コンテキスト
    民間最終のAccessControlContext ACCは、
    //キャッシュは、シーケンシャルストレージの例によれば、サービス・プロバイダがインスタンス化された
    専用のLinkedHashMap <文字列、S> =新しい新しいプロバイダのLinkedHashMap <>(); 
    //イテレータ
    プライベートLazyIterator lookupIterator; 
}

 

//服务提供者查找的迭代器
公共イテレータ<S>反復子(){ 
    戻り新しいイテレータ<S>(){ 
        イテレータ<のMap.Entry <文字列、S >> knownProviders 
            = providers.entrySet()反復子()。
        //のhasNext方法
        パブリックブールのhasNext(){ 
            IF(knownProviders.hasNext())
                trueを返します。
            lookupIterator.hasNextを返します(); 
        } 
        //次の方法
        (次のパブリックS){ 
            IF(knownProviders.hasNext()) 
                )(knownProviders.next()のgetValueを返します。
            lookupIterator.nextを返します(); 
        } 
    }。
}

 

//サービスプロバイダイテレータ検索
プライベートクラスLazyIterator実装するIterator <S> { 
    //サービスプロバイダインタフェース
    、クラス<S>サービス
    //クラスローダ
    クラスローダローダー; 
    //保存したURLの実装クラスの
    列挙を<URL>のconfigsヌル=; 
    //実装するクラスのフルネーム保存
    イテレータを<文字列>保留中のヌル=; 
    //完全なクラス名を達成するために、次のイテレータ
    列nextName =ヌル; 
 
    パブリックブールのhasNext(){ 
        IF(!nextName = NULL){ 
            trueを返すように; 
        } 
        IF(コンフィグ== NULL){ 
            試み{ 
                文字列+ service.getNameのfullName PREFIX =(); 
                IF(ローダ== NULL)
                    コンフィグ= ClassLoader.getSystemResources(のfullName)。
                    のconfigs = loader.getResources(のfullName)。
            }キャッチ(IOExceptionをx)から{ 
                (X、サービス、 "設定ファイルを配置エラー")が失敗。
            } 
        } 
        ((ペンディング== NULL)|| pending.hasNext()!){ながら
            {(configs.hasMoreElements()!)であれば
                falseを返します。
            } 
            保留=パース(サービス、configs.nextElement())。
        } 
        nextName = pending.next()。
        trueを返します。
    } 
 
    公共S次の(){ 
        IF(!のhasNext()){
            新しいはNoSuchElementExceptionを投げます();  
            "+(サービス、 "プロバイダ" + CNフェイル
        } 
        文字列CN = nextName。
        nextName = NULL; 
        クラスC = nullを<?>; 
        {試みる
            C = Class.forNameの(CN、偽、ローダー); 
        }キャッチ(ClassNotFoundExceptionがx)から{ 
            (+ "見つかりません"サービス、 "提供" + CN)を失敗。
        } 
        (!service.isAssignableFrom(C))なら、{ 
            (+ "ではないサブタイプ"サービス、 "提供" + CN)を失敗。
        } 
        {試みる
            S P = service.cast(c.newInstance())。
            providers.put(CN、P)。
            Pを返します。
        }キャッチ(ThrowableをX){ 
        } 
        )(新しいエラーをスローします。//この問題が発生することはできません
    } 
}

まず、 ServiceLoaderは、それがプロパティイテレータを持っているので、ここではのhasNext、次のイテレータを達成するための主な方法があり、反復処理可能インターフェイスを実装しています。hasNextおよび対応する方法は、ここで主に次のコールlookupIteratorあり、lookupIteratorは遅延ロードイテレータです。

第二に、のhasNextメソッドLazyIterator静的変数PREFIXを使用すると、クラスパス/サービス/ディレクトリの下にMETA-INFという名前のファイルにサービス・インターフェースを作成する必要がある理由である「META-INF /サービス/」ディレクトリです。

最後に、反射法にClass.forName()、クラスのオブジェクトをロードする方法と、クラスおよびオブジェクト・クラス・キャッシュ・プロバイダのインスタンスをインスタンス(のLinkedHashMap <文字列、S>型)と、オブジェクトのインスタンスを返すのnewInstanceを使用することによって。

の第五に、不足

1.オンデマンドでロードすることはできません、あなたはすべての実装を横断して、インスタンス化し、その後、私たちはループ内で検索する必要があることを認識する必要があります。あなたが特定のクラスを達成したい、または特定のクラスのインスタンス化に時間がかかりれない場合は、また、廃棄物をもたらした、ロードおよびインスタンス化することができます。

2.実装クラスは柔軟ではない、それだけでいくつかのパラメータに基づいて、対応する実装クラスを取得するのではなく、イテレータのフォームを介して取得することができます取得します。

3.複数の同時マルチスレッド利用クラスServiceLoaderのインスタンスは危険です。

第六に、避けます

上記の欠点ポイント、我々SPI選択メカニズムについては、SPI機構ダボが達成使用することを検討してください。

特定の参照:  http://dubbo.apache.org/zh-cn/blog/introduction-to-dubbo-spi.html

もっとお楽しみに  生体内のインターネット技術の  マイクロチャネル公共数

注:マイクロ・シグナルとの記事を転載してください:labs2020  連絡を。

おすすめ

転載: www.cnblogs.com/vivotech/p/11431353.html