繁体   English   中英

Java9模块:如何根据某些执行优先级执行Provider?

[英]Java9 modules : How to execute Provider based on some priority of execution?

我正在使用 java 9 模块来实现提供程序,我有多个服务接口提供程序。 我想为提供者使用findFirst();

Optional<ServiceInterface> loader=ServiceLoader.load(ServiceInterface.class).findFirst();

我有如下service-interface模块,

服务接口.Java

 public interface ServiceInterface {
    
        int theAnswer();
    
    }

模块信息.java

module ServiceInterface {
    exports com.si.serviceinterface;
}

我有provider-module ,它有两个服务接口实现,

提供者1.java

public class Provider1 implements ServiceInterface {
    @Override
    public int theAnswer() {
        return 42;
    }
}

提供者2.java

public class Provider2 implements ServiceInterface {
  @Override
  public int theAnswer() {
    return 52;
  }
}

模块信息.java

module Provider {
  requires ServiceInterface;
  provides ServiceInterface with Provider1, Provider2;
}

现在,我有consumer-module ,它将使用ServiceLoader来加载提供者。 而不是使用findFirst()来加载服务提供者。 我想根据一些优先级示例进行加载,我想加载 Provider2,然后我应该能够加载而不是加载提供程序 1。

消费者.java

public class Consumer {

  public static void main(String[] args) {
    ServiceLoader<ServiceInterface> loader = ServiceLoader.load(ServiceInterface.class);
    for (final ServiceInterface service : loader) {
      System.out.println("The service " + service.getClass().getSimpleName() + " answers " + service.theAnswer());
    }
    
    
  }
}

任何关于实现优先加载提供程序而不是使用findFirst()的建议。

ServiceLoader文档设计服务部分说

[...] 有两个一般准则:

  • 服务应根据需要声明尽可能多的方法,以允许服务提供者传达其特定领域的属性和其他实现质量因素。 获得服务的服务加载器的应用程序然后可以在服务提供者的每个实例上调用这些方法,以便为应用程序选择最佳提供者。

  • 服务应该表达它的服务提供者是打算作为服务的直接实现还是作为诸如“代理”或“工厂”之类的间接机制。 当特定领域的对象实例化相对昂贵时,服务提供者往往是间接机制; 在这种情况下,服务应该被设计成服务提供者是抽象的,可以按需创建“真正的”实现。 例如, CodecFactory服务通过其名称表示其服务提供者是编解码器的工厂,而不是编解码器本身,因为生产某些编解码器可能很昂贵或复杂。

按照这个指南,我们可以简单地在接口中添加一个优先级查询方法来匹配项目符号并保持其他内容不变,因为实例化并不昂贵。

public interface ServiceInterface {
    int theAnswer();
    int priority();
}
public class Provider1 implements ServiceInterface {
    @Override
    public int theAnswer() {
        return 42;
    }

    @Override
    public int priority() {
        return 0;
    }
}
public class Provider2 implements ServiceInterface {
    @Override
    public int theAnswer() {
        return 52;
    }

    @Override
    public int priority() {
        return 1;
    }
}
public class ServiceConsumer {
    public static void main(String[] args) {
        ServiceLoader<ServiceInterface> loader=ServiceLoader.load(ServiceInterface.class);
        ServiceInterface service = loader.stream().map(Provider::get)
            .max(Comparator.comparingInt(ServiceInterface::priority)).orElseThrow();

        System.out.println("The service " + service.getClass().getSimpleName()
            + " answers " + service.theAnswer());
    }
}
The service Provider2 answers 52

但由于这只是一个示例,您的用例可能涉及创建成本高昂的服务实例。 在这种情况下,您可以按照建议将服务提供者接口和实际服务分开,就像大多数 JDK 服务一样。

public interface ServiceProviderInterface {
    /** Potentially expensive service instantiation */
    ServiceInterface getService();

    /** Can be cheaply queried without calling the expensive method */
    int priority();
}
public interface ServiceInterface {
    /**
     * The operation
     */
    int theAnswer();

    /**
     * Decide yourself if getting the provider is useful, e.g. java.nio.file.FileSystem
     * has such a method, java.nio.charset.Charset has not.
     */
    ServiceProviderInterface provider();
}
public class Provider1 implements ServiceProviderInterface {

    public static class ActualService implements ServiceInterface {
        private final ServiceProviderInterface provider;

        public ActualService(Provider1 p) {
            provider = p;
            System.out.println("potentially expensive Provider1.ActualService()");
        }
        @Override
        public int theAnswer() {
            return 42;
        }
        @Override
        public ServiceProviderInterface provider() {
            return provider;
        }
    }

    @Override
    public ServiceInterface getService() {
        return new ActualService(this);
    }

    @Override
    public int priority() {
        return 0;
    }
}
public class Provider2 implements ServiceProviderInterface {

    public static class ActualService implements ServiceInterface {
        private final ServiceProviderInterface provider;

        public ActualService(Provider2 p) {
            provider = p;
            System.out.println("potentially expensive Provider2.ActualService()");
        }
        @Override
        public int theAnswer() {
            return 52;
        }
        @Override
        public ServiceProviderInterface provider() {
            return provider;
        }
    }

    @Override
    public ServiceInterface getService() {
        return new ActualService(this);
    }

    @Override
    public int priority() {
        return 1;
    }
}

当然,必须调整module-info声明以提供或使用ServiceProviderInterface而不是ServiceInterface 用例现在看起来像

public class ServiceConsumer {
    public static void main(String[] args) {
        ServiceInterface service = ServiceLoader.load(ServiceProviderInterface.class)
            .stream()
            .map(Provider::get)
            .max(Comparator.comparingInt(ServiceProviderInterface::priority))
            .map(ServiceProviderInterface::getService)
            .orElseThrow();

        System.out.println("The service " + service.getClass().getSimpleName()
            + " answers " + service.theAnswer());
    }
}

得到相同的结果,但没有实例化Provider1.ActualService() 只有实际使用的Provider2.ActualService()被实例化。


作为文档指南的替代方案,您可以使用带有注释的第一种方法而不是priority()方法。

public interface ServiceInterface {
    int theAnswer();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Priority {
    int value();
}
@Priority(0)
public class Provider1 implements ServiceInterface {
    public Provider1() {
        System.out.println("potentially expensive Provider1()");
    }

    @Override
    public int theAnswer() {
        return 42;
    }
}
@Priority(1)
public class Provider2 implements ServiceInterface {
    public Provider2() {
        System.out.println("potentially expensive Provider2()");
    }

    @Override
    public int theAnswer() {
        return 52;
    }
}
public class ServiceConsumer {
    public static void main(String[] args) {
        ServiceInterface service = ServiceLoader.load(ServiceInterface.class).stream()
            .max(Comparator.comparingInt(p->p.type().isAnnotationPresent(Priority.class)?
                    p.type().getAnnotation(Priority.class).value(): 0))
            .map(Provider::get)
            .orElseThrow();

        System.out.println("The service " + service.getClass().getSimpleName()
            + " answers " + service.theAnswer());
    }
}

这可以避免潜在的昂贵的实例化,而无需处理两个接口,但是,您可以在实例化之前声明和查询的属性仅限于编译时常量。

另一方面,这种方法可以用来扩展已经存在的服务框架,因为提供接口的模块不需要知道注解。 可以将它们作为某些服务实现和使用站点之间的扩展合同引入。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM