Java SPI機構からSpring Boot SPI拡張まで実現しました。


Java SPI実現
前の記事を参照できます。https://blog.csdn.net/shang_xs/articale/detail/86560499
Spring Boot SPI実現
1.SPIの特徴
異なる実装を選択して、具体的なコードを実行し、SPIインターフェースを定義し、SPIの使用姿勢(前提)を選択して、プロキシクラスのFactoryBenを生成することができます。
public interface ISpi<T> {
boolean verify(T condition);
}
上記の実現を見て疑問に思うのですが、もしいくつかの種類がこの条件を満たしていたらどうすればいいですか?したがって、優先度が一番高い一致者を返します。
public interface ISpi<T> {
 boolean verify(T condition);
/**
 *   ,    ,     
 * @return
 */
default int order() {
    return 10;
}
インターフェースの定義の後、利用者はどうやって使うべきですか?
2.制約spiを使用して実現される制約
JDKのプロキシモードに基づいて、最大の前提はインターフェースによってのみプロキシクラスを生成することです。だからSPIを使用する時、ユーザーはまず一つのインターフェースを定義してISpiを継承してから、具体的なSPIを実現すればいいです。
第二に、Springの生態の下で、すべてのSPIの実現がBeanであることを要求します。自動スキャンまたは注釈方式の声明を配置する必要があります。
3.spi使用の制約
SPIインターフェースを使う時、インターフェースを通して導入します。実際に注入したのは代理種類ですので、具体的な実現類は書かないでください。
4.すべてのSPI実現類を取得する
(org.springframe ewark.beans.factory.Listable BenFactory咻get BensOfType(java.lass)は、jdkを通じて代理類を生成し、エージェント類の中で全てのSPIを遍歴して実現し、導入された最初のパラメータを条件としてマッチングし、最初の命中したSPI実現類を見つけて、上記の具体的なステップを実行します。
public class SpiFactoryBean<T> implements FactoryBean<T> {
private Class<? extends ISpi> spiClz;

private List<ISpi> list;

public SpiFactoryBean(ApplicationContext applicationContext, Class<? extends ISpi> clz) {
    this.spiClz = clz;

    Map<String, ? extends ISpi> map = applicationContext.getBeansOfType(spiClz);
    list = new ArrayList<>(map.values());
    list.sort(Comparator.comparingInt(ISpi::order));
}

@Override
@SuppressWarnings("unchecked")
public T getObject() throws Exception {
    // jdk       
    InvocationHandler invocationHandler = new InvocationHandler() {
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            for (ISpi spi : list) {
                if (spi.verify(args[0])) {
                    //            
                    return method.invoke(spi, args);
                }
            }

            throw new NoSpiChooseException("no spi server can execute! spiList: " + list);
        }
    };

    return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{spiClz},
            invocationHandler);
}

@Override
public Class<?> getObjectType() {
    return spiClz;
}
例示的なプレゼンテーションの設計の後は、実現されるべきであるが、実現はあまりにも簡単であるため、設計の過程で、ついでに書きました。つまり、上述のインターフェース定義ISpiと、動的なエージェントを生成するためのSpiFactoryBenは次に簡単な実例を書いて、機能のプレゼンテーションに用いて、一つのIPrintを定義してテキスト出力に用いて、二つの実装を行います。コンソール出力、ログ出力
public interface IPrint extends ISpi<Integer> {

default void execute(Integer level, Object... msg) {
    print(msg.length > 0 ? (String) msg[0] : null);
}

void print(String msg);
        ,       execute      ,  level<=0@Component public class ConsolePrint implements IPrint { @Override public void print(String msg) { System.out.println("console print: " + msg); }

@Override
public boolean verify(Integer condition) {
    return condition <= 0;
}
}

@Slf4j @Component public class LogPrint implements IPrint { @Override public void print(String msg) { log.info("log print: {}", msg); }

@Override
public boolean verify(Integer condition) {
    return condition > 0;
}
前の手順は普通の書き方と変わらないですが、使う姿勢はどうですか?
@SpringBootApplication public class Application {

public Application(IPrint printProxy) {
    printProxy.execute(10, " log print ");
    printProxy.execute(0, " console print ");
}

public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
}
上記のApplicationの構造方法を見ると、IPrintパラメータが入ることが要求されます。Springは容器の中からbeanを見つけてパラメータとして入ってきます。このbeanは私達が生成したエージェント類です。これでこそ、異なるパラメータによって具体的な実装クラスを選択できます。
このプロキシ類をどのように宣言するかということです。FactoryBean方式でBeanを宣言し、@Primary注解を加えると、注入を確実にすることができます。これは声明のプロキシ類です。
@Configuration public class PrintAutoConfig {
@Bean
public SpiFactoryBean printSpiPoxy(ApplicationContext applicationContext) {
    return new SpiFactoryBean(applicationContext, IPrint.class);
}

@Bean
@Primary
public IPrint printProxy(SpiFactoryBean spiFactoryBean) throws Exception {
    return (IPrint) spiFactoryBean.getObject();
}



@SpringBootApplication
public class SpringBootApplication {
public SpringBootApplication (Iprint pringProxy){
    pringProxy.execute(10,"log.print");
    pringProxy.execute(1,"console.print");
   }

public static void main(String[] args) {
    SpringApplication.run(SpringBootApplication.class, args);
  }
}
具体的な実装については、下記を参照してください。https://github.com/dwyanewede/project-learn
拡張点ロード:comp.learn.demo.spi.ISpi