如何以编程方式加载JDK模块?

时间:2018-06-09 17:22:38

标签: java java-module

假设我们有模块A,它可以动态加载模块B(使用类ModuleFinderModuleLayer等)。最后一个要求标准模块java.sql未加载到模块A的引导层中。如何使用java代码从JDK(或JRE)加载所需的java.sql

修改

此示例maven项目演示了我的问题:

项目结构:

│   pom.xml
│
├───loader
│   │   pom.xml
│   │
│   └───src
│       ├───main
│       │   ├───java
│       │   │   │   module-info.java
│       │   │   │
│       │   │   └───app
│       │   │       └───module
│       │   │           └───loader
│       │   │                   AppLoader.java
│       │   │                   AppModule.java
│       │   │
│       │   └───resources
│       └───test
│           └───java
└───sql-module
    │   pom.xml
    │
    └───src
        ├───main
        │   ├───java
        │   │   │   module-info.java
        │   │   │
        │   │   └───app
        │   │       └───module
        │   │           └───sql
        │   │                   SQLAppModule.java
        │   │
        │   └───resources
        └───test
            └───java

的pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>sample-app</artifactId>
        <groupId>sample-app</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>loader</artifactId>
</project>

装载器/ pom.xml中:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>sample-app</artifactId>
        <groupId>sample-app</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>loader</artifactId>
</project>

装载器/ SRC /主/爪哇/ module-info.java:

module app.module.loader {
    exports app.module.loader;
    uses AppModule;
}

装载器/ SRC /主/ JAVA /应用程序/模块/装载器/ AppLoader.java:

public class AppLoader {
    public static void main(String[] args) {
        var path = Paths.get("sql-module", "target", "classes");
        var moduleFinder = ModuleFinder.of(path);
        var boot = ModuleLayer.boot();
        var config = boot.configuration().resolveAndBind(moduleFinder, ModuleFinder.of(), Collections.emptyList());
        var newLayer = boot.defineModulesWithOneLoader(config, Thread.currentThread().getContextClassLoader());
        var testModule = ServiceLoader.load(newLayer, AppModule.class)
                .findFirst()
                .orElseThrow(() -> new RuntimeException("Module not found!"));
        System.out.println("Module name: " + testModule.name());
        System.out.println("Module version: " + testModule.version());
    }
}

装载器/ SRC /主/ JAVA /应用程序/模块/装载器/ AppModule.java:

public interface AppModule {
    String name();
    String version();
}

SQL模块/ pom.xml中:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <artifactId>sample-app</artifactId>
        <groupId>sample-app</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <artifactId>sql-module</artifactId>
    <dependencies>
        <dependency>
            <groupId>sample-app</groupId>
            <artifactId>loader</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
</project>

SQL模块/ SRC /主/爪哇/ module-info.java:

module app.module.sql {
    requires app.module.loader;
    requires java.sql;

    provides AppModule with SQLAppModule;
}

SQL模块/ SRC /主/ JAVA /应用程序/模块/ SQL / SQLAppModule.java:

public class SQLAppModule implements AppModule {
    public SQLAppModule() {
        List<Driver> drivers = DriverManager.drivers().collect(Collectors.toList());
        System.out.println("Drivers on class path: " + drivers.size());
        drivers.forEach(d -> {
            System.out.println("Driver: " + d.toString());
            System.out.println("Version: " + d.getMajorVersion() + "." + d.getMinorVersion());
        });
    }

    @Override
    public String name() {
        return "SQL Module";
    }

    @Override
    public String version() {
        return "1.0-SNAPSHOT";
    }
}

当您尝试使用AppLauncher中的main启动应用程序时,您将收到错误(我现在使用jdk-10.0.1):

Exception in thread "main" java.lang.module.FindException: Module java.sql not found, required by app.module.sql
    at java.base/java.lang.module.Resolver.findFail(Resolver.java:877)
    at java.base/java.lang.module.Resolver.resolve(Resolver.java:191)
    at java.base/java.lang.module.Resolver.bind(Resolver.java:297)
    at java.base/java.lang.module.Configuration.resolveAndBind(Configuration.java:482)
    at java.base/java.lang.module.Configuration.resolveAndBind(Configuration.java:288)
    at app.module.loader/app.module.loader.AppLoader.main(AppLoader.java:14)

hacks怎么样:我认为它们必须在最后一个地方使用,所以我们试图找到更多或更少的官方&#34;现在就这样做。

This answer 无法解决此问题,因为启动时layer.findModule(moduleName).orElse(null)或任何其他图层将返回null。

1 个答案:

答案 0 :(得分:0)

您不能也不应该在运行时这样做。

可能会获取“ app.module.sql”模块所需的缺少的模块:

var missingModuleNames = moduleFinder.find("app.module.sql")
                                     .map(ModuleReference::descriptor)
                                     .map(ModuleDescriptor::requires)
                                     .orElse(Collections.emptySet())
                                     .stream()
                                     .map(ModuleDescriptor.Requires::name)
                                     .filter(name -> boot.findModule(name).isEmpty())
                                     .collect(Collectors.toSet());

您甚至可以为Java平台模块创建ModuleFinder

var platformModules = Files.list(Paths.get(URI.create("jrt:/modules")))
                           .collect(Collectors
                               .toMap(AppLoader::getModuleName, Function.identity()));

var missingModulePaths = missingModules.stream()
                                       .filter(systemModules::containsKey)
                                       .map(systemModules::get)
                                       .toArray(Path[]::new);

var missingModuleFinder = ModuleFinder.of(missingModulePaths);

但是,即使您进行此递归操作(java.sql需要java.transaction.xa),只要尝试定义以下内容,尝试加载任何平台模块的尝试都将失败,并显示LayerInstantiationException。模块:

var cfg = boot.configuration()
              .resolveAndBind(missingModuleFinder, ModuleFinder.of(), missingModules);

// This will throw the exception, because 'a layer cannot be created if the 
// configuration contains a module named "java.base", or a module contains a 
// package named "java" or a package with a name starting with "java.".'
// (see Javadoc of ModuleLayer#defineModulesWithOneLoader(Configuration, List<ModuleLayer>, ClassLoader)
ModuleLayer.defineModulesWithOneLoader(cfg, List.of(boot), null);