使用 NIO 在外部 JAR 中加载所有 类
Loading all classes in an external JAR using NIO
我有一个包含一些 JAR 的插件文件夹,我想加载它们的所有 *.class。我看到很多关于 JarFile
或 ZipEntry
的答案。但是,我想使用 java.nio 包而不是 io.
我能做什么:
- 使用
Files.list(Path)
列出文件夹中的文件(例如列出我所有的 jar)
- 加载 JAR 中的 .class,知道其在所述 JAR 中的确切位置。
我还不能做什么:
- 使用 NIO 在 JAR/ZIP 中列出文件。
最终,我想要:
- 遍历几个 JAR 中的每个 class 并加载它们以寻找一种插件系统的注解
我应该补充一点,我不需要使用像 Guava 或 Apache 这样的 API 的工作解决方案,但我只想知道它是如何完成的。
我当前的代码:
import java.io.IOException;
import java.nio.file.*;
import java.security.SecureClassLoader;
import java.util.*;
public class PluginManager {
public final Path pluginFolder;
public PluginManager(Path pluginFolder) {
this.pluginFolder = pluginFolder;
}
public void load() {
final var pluginClassLoader = new SecureClassLoader(getClass().getClassLoader()){
FileSystem jar;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String pluginClassName = jar.getSeparator() + name.replaceAll("\.", jar.getSeparator()) + ".class";
try{
byte[] bytes = Files.readAllBytes(jar.getPath(pluginClassName));
return defineClass(name, bytes, 0, bytes.length);
}catch(IOException e){
e.printStackTrace();
}
throw new ClassNotFoundException("Failed to found "+name);
}
};
try(Stream<Path> files = Files.list(pluginFolder)){
files.filter((file) -> !Files.isDirectory(file) &&
(file.toString().endsWith(".jar") || file.toString().endsWith(".zip")))
.forEach(jar -> {
try(FileSystem fileSystem = FileSystems.newFileSystem(jar)){
pluginClassLoader.jar = fileSystem;
loadPlugin(fileSystem, pluginClassLoader);
}catch(IOException | ClassNotFoundException e){
e.printStackTrace();
}
});
}catch(IOException e){
e.printStackTrace();
throw new IllegalStateException("Failed to scan plugin folder. Cannot continue.");
}
}
private void loadPlugin(FileSystem jar, SecureClassLoader classLoader) throws IOException, ClassNotFoundException{
String className = ... // here, we retrieve the plugin class name from a manifest file using FileSystem.getPath().
// what I want is to get rid of that manifest and to load and test every class.
Class<?> clazz = classLoader.loadClass(className);
// do stuff with loaded class
}
我想避免 java.io 这样的代码:
How to load Classes at runtime from a folder or JAR?
我偶然发现了这个 List of files inside a zip folder and its subfolder。我最初错过这种方法的地方不是使用文件夹中的 jar 路径调用 Files.walkFileTree(Path, FileVisitor)
,而是使用创建的 FileSystem
的根路径(使用 FileSystem.getPath("/")
)。这非常适合我。
我有一个包含一些 JAR 的插件文件夹,我想加载它们的所有 *.class。我看到很多关于 JarFile
或 ZipEntry
的答案。但是,我想使用 java.nio 包而不是 io.
我能做什么:
- 使用
Files.list(Path)
列出文件夹中的文件(例如列出我所有的 jar)
- 加载 JAR 中的 .class,知道其在所述 JAR 中的确切位置。
我还不能做什么:
- 使用 NIO 在 JAR/ZIP 中列出文件。
最终,我想要:
- 遍历几个 JAR 中的每个 class 并加载它们以寻找一种插件系统的注解
我应该补充一点,我不需要使用像 Guava 或 Apache 这样的 API 的工作解决方案,但我只想知道它是如何完成的。
我当前的代码:
import java.io.IOException;
import java.nio.file.*;
import java.security.SecureClassLoader;
import java.util.*;
public class PluginManager {
public final Path pluginFolder;
public PluginManager(Path pluginFolder) {
this.pluginFolder = pluginFolder;
}
public void load() {
final var pluginClassLoader = new SecureClassLoader(getClass().getClassLoader()){
FileSystem jar;
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String pluginClassName = jar.getSeparator() + name.replaceAll("\.", jar.getSeparator()) + ".class";
try{
byte[] bytes = Files.readAllBytes(jar.getPath(pluginClassName));
return defineClass(name, bytes, 0, bytes.length);
}catch(IOException e){
e.printStackTrace();
}
throw new ClassNotFoundException("Failed to found "+name);
}
};
try(Stream<Path> files = Files.list(pluginFolder)){
files.filter((file) -> !Files.isDirectory(file) &&
(file.toString().endsWith(".jar") || file.toString().endsWith(".zip")))
.forEach(jar -> {
try(FileSystem fileSystem = FileSystems.newFileSystem(jar)){
pluginClassLoader.jar = fileSystem;
loadPlugin(fileSystem, pluginClassLoader);
}catch(IOException | ClassNotFoundException e){
e.printStackTrace();
}
});
}catch(IOException e){
e.printStackTrace();
throw new IllegalStateException("Failed to scan plugin folder. Cannot continue.");
}
}
private void loadPlugin(FileSystem jar, SecureClassLoader classLoader) throws IOException, ClassNotFoundException{
String className = ... // here, we retrieve the plugin class name from a manifest file using FileSystem.getPath().
// what I want is to get rid of that manifest and to load and test every class.
Class<?> clazz = classLoader.loadClass(className);
// do stuff with loaded class
}
我想避免 java.io 这样的代码: How to load Classes at runtime from a folder or JAR?
我偶然发现了这个 List of files inside a zip folder and its subfolder。我最初错过这种方法的地方不是使用文件夹中的 jar 路径调用 Files.walkFileTree(Path, FileVisitor)
,而是使用创建的 FileSystem
的根路径(使用 FileSystem.getPath("/")
)。这非常适合我。