ホームページ  >  記事  >  Java  >  SPI を使用して Java でデカップリングを実現する方法

SPI を使用して Java でデカップリングを実現する方法

王林
王林転載
2023-05-19 21:58:151351ブラウズ

概要

SPI の正式名は Service Provider Interface で、フレームワークの拡張機能やコンポーネントの置き換えを開始するために使用できます。

本質は、インターフェイス実装戦略モード構成ファイルを使用して実装クラスを動的にロードすることです。

具体的な使用方法では、いくつかの規則があります。

(1) インターフェースのフルネームファイルは、classPath

# の META-INF/services/ 配下に作成されると規定されています。 ## (2) このファイルには、インタフェース実装クラスのフルネーム(パスファイル名)を記述します(実装クラスが複数ある場合は、行を分けて記述します)。

(3) 2を利用する場合は、java.util.ServiceLoaderのload(Interface.class)で実装クラスを取得することで利用可能になります。

インターフェイス実装クラスにはパラメーターのないコンストラクターが必要であることに注意してください。

実装ケース

このアプリケーションでは、A モジュールと B モジュールの 2 つのモジュールがあり、この 2 つのモジュールのうち、A モジュールがメイン モジュール、B がスレーブ モジュール、B モジュールが存在します。モジュールはモジュール A に依存します。しかし現在、モジュール B に実装されているクラスが存在します。モジュール A はこのクラスの関数を呼び出す必要があり、モジュールはモジュール B に依存できなくなります。この時点で、デカップリングが必要です。この実装では、デカップリング実装に SPI が使用されます。具体的な実装計画は次のとおりです:

(1) モジュール A に新しいインターフェイスを作成します: MyLogAppender. 具体的な実装は次のとおりです:

/**
 * @author Huang gen(kenfeng)
 * @description 自定义的appender接口
 * @Since 2021/02/21
 **/

public interface MyLogAppender {

    /**
     * 获取实现的appender
     * @return  返回新建的appender对象
     * */
    Appender getAppender();
}

このインターフェイスは非常に単純で、アペンダー オブジェクトを返すだけです。オブジェクトの実際の操作については、インターフェースの実装内で操作が実行されます。

(2) このインターフェイスの実装をモジュール B に追加します。具体的な操作は次のとおりです。

/**
 * @author Huang gen(kenfeng)
 * @description 自定义的appender
 * @Since 2021/02/21
 **/
@Component
public class MeshLogAppender extends UnsynchronizedAppenderBase<ILoggingEvent> implements MyLogAppender,ApplicationContextAware {

    private ApplicationContext applicationContext;

    public MeshLogAppender(){ }

    @Override
    public Appender getAppender() {
        MeshLogAppender meshLogAppender = new MeshLogAppender();
        return meshLogAppender;
    }

    @Override
    protected void append(ILoggingEvent iLoggingEvent) {
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        String std = simpleDateFormat.format(new Date(Long.parseLong(String.valueOf(iLoggingEvent.getTimeStamp()))));
        String log = std + "\t" + iLoggingEvent.getLevel() +"\t"+"--- ["+ iLoggingEvent.getThreadName()+"]\t"+iLoggingEvent.getCallerData()[0]+":\t "+iLoggingEvent.getMessage();
        FlowMessage input = new FlowMessage();
        MeshFlowService meshFlowService = SandboxSystemServiceFactory.getService(MeshFlowService.class);
        Map<String, Object> body = new HashMap<>(2);
        body.put("log",log);
        input.setTenantCode(DefaultTenant.get());
        input.setAppCode("epoch");
        input.setFlowCode("log_broadcast");
        input.setBody(body);
        FlowMessage output = meshFlowService.process(input);
        if(!StringUtils.isEmpty(output.getErrorMessage())){
            throw new RuntimeException("发布日志时,广播失败");
        }
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

このインターフェイスの宣言とインターフェイスの実装には、いくつかの小さなトリックがあります。埋め込む。インターフェースではクラスの取得が宣言されているだけで、具体的なメソッドは実装されていません。実装クラスでは、このクラスをインスタンス化し、新しいクラスを作成して返しますが、このとき、ユーザーはこの get メソッドに従って実装クラスを取得し、実装クラスに対して何らかの操作を実行できます。この方法で記述すると、次の 2 つの利点がもたらされます: i. コードがより簡潔になり、インターフェイス コードがシンプルで理解しやすくなります ii. 一部のパラメータを実装クラスの構築メソッドに注入できます。 get メソッドに直接注入できます。

(3) 実装クラスが配置されているフォルダー (sandbox-app-epoch-starter) に構成ファイルを追加します。構成ファイルのデフォルトのパスは、resources/META-INF/services です。 /, in このフォルダーに新しい質問を作成します。ファイル名はインターフェイスへのパス、内容は実装クラスへのパスです。これから、インターフェース-->実装クラスのマッピングを実装できます。

上の図に示すように、ファイル名は com.alibaba.halo.sandbox.app.util.MyLogAppender

ファイルの内容は com.alibaba.lattice2 です。 epoch.util.MeshLogAppender

原則として、ユーザーがインターフェイスを使用すると、プロジェクト内のすべてのファイルがスキャンされ、ファイル名は com.alibaba.halo.sandbox.app.util.MyLogAppender になります。関連する実装クラス

(4) A では、インターフェイスを直接使用して呼び出しを行うことができます。具体的な実装は次のとおりです:

				ServiceLoader<MyLogAppender> myLoaderInterfaceServiceLoader = ServiceLoader.load(MyLogAppender.class);
        Iterator<MyLogAppender> myLoaderInterfaceIterator = myLoaderInterfaceServiceLoader.iterator();
        while (myLoaderInterfaceIterator.hasNext()){
            MyLogAppender myLoaderInterface = myLoaderInterfaceIterator.next();

            Appender newAppender = myLoaderInterface.getAppender();
            newAppender.setName("application");
            newAppender.setContext(loggerContext);
            newAppender.start();
            rootLogger.addAppender(newAppender);
        }

As上記からわかるように、これを使用して MyLogAppender インターフェイスを直接呼び出すことができ、インターフェイスを通じて取得した Appender を後で直接割り当てることができます。

メリットとデメリット

メリット: コードの分離が実現可能

デメリットは、実装クラスが複数ある場合、インスタンスの取得にパラメーターやフラグを渡すことができないことです。トラバーサルを通じてのみ取得でき、遅延読み込みは実装できません

以上がSPI を使用して Java でデカップリングを実現する方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。