java – 如何以编程方式加载JDK模块?
作者:互联网
假设我们有模块A动态加载模块B(使用类ModuleFinder,ModuleLayer等).最后一个需要标准模块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目录/主/ JAVA / 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:我认为它们必须在最后一个地方使用,所以我们试图找到或多或少的“官方”方式来实现它.
This answer无法解决此问题,因为启动时或任何其他层上的layer.findModule(moduleName).orElse(null)将返回null.
解决方法:
你不能也不应该在运行时这样做.
可以获取“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);
标签:java-module,java 来源: https://codeday.me/bug/20190910/1799612.html