提问者:小点点

ServiceLoader真的动态加载提供程序吗?


我最近发现矛盾留档是否服务加载器将定位提供程序添加到模块路径后引导。

ServiceLoader::重新加载:

public void reload​()

清除此加载器的提供程序缓存,以便重新加载所有提供程序。调用此方法后,迭代器或流方法的后续调用将从头开始延迟定位提供程序(并在迭代器的情况下实例化),就像新创建的服务加载器所做的那样。

此方法适用于可以将新服务提供程序安装到正在运行的Java虚拟机中的情况。

这清楚地表明服务解析是完全动态的。

另一方面ModuleFinder::findAll与它相矛盾。
"ModuleFinder用于在解析或服务绑定期间查找模块。"-Javadoc

Set<ModuleReference> findAll​()

返回此查找器可以定位的所有模块引用的集合。ModuleFinder提供其定位的模块的一致视图。如果多次调用findAll,则每次都将返回相同的(等于)结果。对于返回集合中的每个Module参考元素,如果调用查找该模块,则确保查找将定位Module参考。

根据这句话,分辨率固定在模块层创建时,这实际上是预期的,因为整个Java平台模块系统在设计上是静态的。如果新的提供者需要其他模块,它将不得不修改现有的模块图。

所以我的问题是:第一个引用是来自Java8 Javadoc的遗留文档,还是真的存在可以动态添加新提供程序的情况?

在这里,我将证明模块查找器文档是正确的:

com. service.Service

package com.service;

import java.util.ServiceLoader;
import java.util.stream.Collectors;

public interface Service {

    public static void main(String[] args) throws InterruptedException {
        ServiceLoader<Service> loader = ServiceLoader.load(Service.class);

        for (int i = 0; i < 5; i++) {
            System.out.print("Attempt " + (i + 1) + ": ");
            System.out.println(loader
                            .stream()
                            .map(ServiceLoader.Provider::type)
                            .map(Object::toString)
                            .collect(Collectors.joining(", ")));

            Thread.sleep(5000);
            loader.reload();
        }
    }
}

module-info

module service {
    exports com.service;
    uses com.service.Service;
}

在不同的模块中,classcom. Provider.Provider

package com.provider;

import com.service.Service;

public class Provider implements Service {

}

module-info

module provider {
    exports com.provider;
    requires service;
    provides com.service.Service with com.provider.Provider;
}

这是一个实时GIF当我第一次在模块路径中没有提供者的情况下运行它时会发生什么。在第二次运行时,提供者已经存在,我将尝试在运行时删除它。


共1个答案

匿名用户

服务提供者API工作在Java类加载器之上,默认情况下,这些类加载器不是动态的。类路径在JVM启动时确定,然后不会更新:您试图删除的JAR由JVM打开,直到关闭才会释放。如果您需要一些不同的行为,您将需要使用自定义类加载器,就像JEE应用程序服务器用于部署webapps或OSGi实现中的类加载器一样。