JAva SPI 01-SPIは何ですか?spi使用入門チュートリアルServiceLoader使用概要

4921 ワード

シリーズディレクトリ
spi 01-spiとは何ですか?使用開始
spi 02-spiの実戦解決slf 4 jパケット衝突問題
spi 03-spi jdkソースコード解析を実現
spi 04-spi dubboソースコード解析を実現
spi 05-dubbo adaptive extension適応拡張
spi 06-自分でゼロから手書きでSPIフレームワークを実現
spi 07-自動生成SPIプロファイル実装方式
問題の導入
以前は、この基準に従って実現させるための基準を指定し、対応する容器を作成したいと思っていました.
その後、コードでこれらの実装を動的に取得し、コードを実行します.
困難
インタフェースの実装をどのように取得しますか?
初歩案
同僚と議論するのは、パッケージのclassをスキャンする方法です.次に、カスタム基準のサブクラスであるかどうかを判断します.
  • 欠点
  • 違和感を感じ,デッドインプリメンテーションクラスのパッケージ名を限定する必要があり,性能も劣る.
    SPIの解決方法
    今日はhibernate-validatorソースコードを読んで啓発されました.
    SPI方式で、より自然にこの問題を解決することができます.
    SPIはService Provider Interfacesの略です.
    本文は簡単にどのように使うかを紹介して、具体的な原理、しばらく深く研究しません.
    SPIって何?
    SPIはJavaが提供するサービスロード方式で、サービスプロバイダインターフェースというフルネームです.
    JavaのSPI仕様に従って、対応するインプリメンテーションによって提供されるサービスインタフェース、すなわちサービスプロバイダを定義することができます.
    そして使用時にSPIの仕様に従って対応するサービスプロバイダのサービス実装を取得する.
    SPIサービスローディングメカニズムによるサービスの登録と発見は,コードに特定のサービスプロバイダを書き込むことを効果的に回避できる.これにより、インタフェースに基づいてプログラミングを行い、モジュール間のデカップリングを実現することができる.
    SPIメカニズムの約束
  • META-INF/services/ディレクトリに、APIの特定のインプリメンテーションクラスのフルリミット名
  • であるインタフェースフルリミット名で命名されたファイルを作成する.
  • ServiceLoaderクラスを使用してMETA-INFのインプリメンテーションクラス
  • を動的にロード
  • SPIの実装クラスがJarである場合、メインプログラムClassPathに
  • を配置する必要がある.
  • APIの特定の実装クラスには、パラメータを持たない構築方法
  • が必要である.
    シーンの適用例
    SPI適用シーンの例
  • JDBC

  • jdbc 4.0以前は、開発者はClass.forName(「xxx」)に基づいてドライバをロードする必要があり、jdbc 4もspiのメカニズムに基づいてドライバプロバイダを発見し、METAINF/services/java.sql.Driverファイルに実装クラスを指定することでドライバプロバイダを暴露することができる.
  • COMMON-LOGGING

  • apacheが最初に提供したログのフェースインタフェース.インタフェースのみで、実装されていません.
    具体的な案は各プロバイダによって実現され、発見ログプロバイダはMETAINF/services/org.apache.commons.logging.LogFactoryプロファイルをスキャンし、そのファイルの内容を読み取ることによってログ提工商実現類を見つける.
    ログインプリメンテーションにこのファイルが含まれており、ログファクトリインタフェースのインプリメンテーションクラスをファイルに作成すればよい.
    単純な実装
    ファイルディレクトリ
    .
    ├── java
    │   └── com
    │       └── github
    │           └── houbb
    │               └── forname
    │                   ├── Say.java
    │                   ├── Sing.java
    │                   └── impl
    │                       ├── DefaultSay.java
    │                       └── DefaultSing.java
    └── resources
        └── META-INF
            └── services
                └── com.github.houbb.forname.Say
    インタフェースと実装の定義
  • Say.java
  • public interface Say {
    
        /**
         *  
         */
        void say();
    
    }
  • DefaultSay.java
  • import com.github.houbb.forname.Say;
    
    public class DefaultSay implements Say {
    
        @Override
        public void say() {
            System.out.println("Default say");
        }
    
    }
    サービス実装指定の作成
    resourcesディレクトリの下に、META-INF/servicesフォルダを作成し、インタフェースのフルパス名com.github.houb.forname.Sayをファイル名、コンテンツを対応するインプリメンテーションクラスのフルパスとします.
    複数であれば、そのまま改行して区切ります.
  • com.github.houbb.forname.Say
  • com.github.houbb.forname.impl.DefaultSay
    テスト
  • SayTest.java
  • public class SayTest {
    
        @Test
        public void spiTest() {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            ServiceLoader loader = ServiceLoader.load(Say.class, classLoader);
    
            for (Say say : loader) {
                say.say();
            }
        }
    
    }
  • 試験結果
  • Default say
    簡単にまとめる
    Javaでは、ServiceLoaderクラスによって、クラスのすべてのサブクラスを容易に見つけることができます.
    META-INF/servicesでのインプリメンテーション指定とインプリメンテーションサブクラスインプリメンテーションは、インタフェース定義と完全に分離できます.
    面倒なところ
    毎回インプリメンテーション指定ファイルを手動で作成するのは煩雑です.
    Autoはこの問題を解決するために生まれた.
    AutoVersionデモ
    maven導入
    
        
            com.google.auto.service
            auto-service
            1.0-rc4
            true
        
    
    インタフェースと定義
  • Sing.java
  • public interface Sing {
    
        /**
         *   
         */
        void sing();
    
    }
  • DefaultSing.java
  • @AutoService(Sing.class)
    public class DefaultSing implements Sing {
    
        @Override
        public void sing() {
            System.out.println("Sing a song...");
        }
    
    }
    テスト
  • SingTest.java
  • public class SingTest {
    
        @Test
        public void spiTest() {
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            ServiceLoader loader = ServiceLoader.load(Sing.class, classLoader);
    
            for (Sing sing : loader) {
                sing.sing();
            }
        }
    
    }
  • 結果
  • Sing a song...
    簡単にまとめる
    Googleのautoにより、コンパイル時に自動的に対応するインタフェースを生成して指定ファイルを実現できます.
    target対応のファイルの下に表示されます.
    実現原理も、比較的簡単です.Javaのコンパイル時に注記して、対応するファイルを生成すればいいです.
    実際にdubboなどのフレームワークでは、SPIメカニズムを利用してプロジェクト全体の柔軟性を向上させることができます.
    JAvaが持っているSPIには多くの不足点があり、本シリーズは使用を学び、自分の強化したSPIフレームワークを実現することです.
    ソースアドレス
    SPIソース
    参考資料
    Oracle SPI-intro
    google auto
    JAVA SPIのメカニズムと使用方法を詳しく理解する