Java 如何以编程方式加载JDK模块?
假设我们有一个模块Java 如何以编程方式加载JDK模块?,java,java-module,Java,Java Module,假设我们有一个模块A,它动态加载模块B(使用类ModuleFinder,ModuleLayer等)。最后一个需要标准模块java.sql,该模块未使用模块A加载到启动层。如何使用java代码从JDK(或JRE)加载所需的java.sql 编辑 此示例maven项目演示了我的问题: 项目结构: │ pom.xml │ ├───loader │ │ pom.xml │ │ │ └───src │ ├───main │ │ ├───java │
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>
loader/src/main/java/app/module/loader/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());
}
}
loader/src/main/java/app/module/loader/AppModule.java:
public interface AppModule {
String name();
String version();
}
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";
}
}
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/main/java/app/module/sql/SQLAppModule.java:
public interface AppModule {
String name();
String version();
}
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";
}
}
那黑客呢:我认为他们必须在最后一个地方使用,所以我们现在尝试找到或多或少的“官方”方式来做
无法解决此问题,因为启动时或任何其他层上的layer.findModule(moduleName).orElse(null)将返回null。您不能也不应该在运行时执行此操作 可以获取“app.module.sql”模块所需的缺失模块:
var missingModuleNames=moduleFinder.find(“app.module.sql”)
.map(模块引用::描述符)
.map(ModuleDescriptor::requires)
.orElse(Collections.emptySet())
.stream()
.map(ModuleDescriptor.Requires::name)
.filter(name->boot.findModule(name).isEmpty())
.collect(收集器.toSet());
您甚至可以为Java平台模块创建一个ModuleFinder
:
var platformModules=Files.list(path.get(URI.create(“jrt:/modules”))
.收藏(收藏家)
.toMap(AppLoader::getModuleName,Function.identity());
var missingModulePaths=missingModules.stream()
.filter(systemModules::containsKey)
.map(systemModules::get)
.toArray(路径[]:新建);
var missingModuleFinder=模块Finder.of(MissingModulePath);
但是,即使您执行此递归操作(java.sql
要求java.transaction.xa
),只要您尝试定义模块,加载任何平台模块的尝试都将失败,并出现LayerInstationException
:
var cfg=boot.configuration()
.resolveAndBind(missingModuleFinder,ModuleFinder.of(),missingModules);
//这将引发异常,因为“如果
//配置包含名为“java.base”的模块,或者模块包含
//名为“java”的包或名称以“java”开头的包
//(参见ModuleLayer的Javadoc#defineModulesWithOneLoader(配置、列表、类加载器)
ModuleLayer.defineModulesWithOneLoader(cfg,List.of(boot)),null;
google展示了这一点,但它看起来像一个黑客。你可以简单地使用一个导入语句……这就是它们构建的目的。通常不需要按时间顺序加载库。你能解释一下你的场景吗?通常Java处理导入,并在不需要的时候加载/卸载它们。对不起,黑客解决方案就是这样被称为reaso的n、 它们通常很难理解,而且通常不是很健壮。黑客是你的最后手段,作为专业人士,你尽可能避免使用。gist上的代码片段确实是一个黑客,因为它正在侵入JDK内部API(使用setAccessible(true)
调用不可见方法)。Java模块的全部目的是阻止这样的代码工作。它目前在JDK 9/10中工作,因为默认选项是--invalize access=permit
,但在将来的某个时候(JDK 11或更高版本),这种方法将停止工作。看起来更像是
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";
}
}
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)