下面的代碼實作了Java 9 +(JigSAW)中的子產品路徑掃描。它查找調用堆棧上的所有類,然後對每個類引用調用
classRef.getModule().getLayer().getConfiguration().modules()
,它傳回A
List
,而不僅僅是
List
. (
ResolvedModule
允許您通路子產品資源,而
Module
沒有。)給定
解析子產品
參考每個子產品,您可以調用
.reference()
方法擷取
ModuleReference
對于一個子產品。
ModuleReference#open()
給你一個
ModuleReader
,它允許您使用
ModuleReader#list()
,或使用
Optional ModuleReader#open(resourcePath)
或
Optional ModuleReader#read(resourcePath)
. 然後關閉
模數轉換器
當你完成了子產品。我所看到的任何地方都沒有記錄。很難搞清楚這一切。但這裡是代碼,希望其他人能從中受益。
注意,即使在jdk9+中,您仍然可以使用傳統的類路徑元素和子產品路徑元素,是以對于完整的子產品路徑+類路徑掃描,您可能應該使用适當的類路徑掃描解決方案,例如
ClassGraph
,它支援使用以下機制進行子產品掃描(免責聲明,我是作者)。
package main;
import java.lang.StackWalker.Option;
import java.lang.module.ModuleReader;
import java.lang.module.ModuleReference;
import java.lang.module.ResolvedModule;
import java.net.URI;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.util.AbstractMap.SimpleEntry;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashSet;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
public class Java9Scanner {
private static final class CallerResolver extends SecurityManager {
@Override
protected Class>[] getClassContext() {
return super.getClassContext();
}
}
private static void findLayerOrder(ModuleLayer layer,
Set visited, Deque layersOut) {
if (visited.add(layer)) {
List parents = layer.parents();
for (int i = 0; i < parents.size(); i++) {
findLayerOrder(parents.get(i), visited, layersOut);
}
layersOut.push(layer);
}
}
private static List> findModuleRefs(
Class>[] callStack) {
Deque layerOrder = new ArrayDeque<>();
Set visited = new HashSet<>();
for (int i = 0; i < callStack.length; i++) {
ModuleLayer layer = callStack[i].getModule().getLayer();
findLayerOrder(layer, visited, layerOrder);
}
Set addedModules = new HashSet<>();
List> moduleRefs = new ArrayList<>();
for (ModuleLayer layer : layerOrder) {
Set modulesInLayerSet = layer.configuration()
.modules();
final List> modulesInLayer =
new ArrayList<>();
for (ResolvedModule module : modulesInLayerSet) {
modulesInLayer
.add(new SimpleEntry<>(module.reference(), layer));
}
// Sort modules in layer by name for consistency
Collections.sort(modulesInLayer,
(e1, e2) -> e1.getKey().descriptor().name()
.compareTo(e2.getKey().descriptor().name()));
// To be safe, dedup ModuleReferences, in case a module occurs in multiple
// layers and reuses its ModuleReference (no idea if this can happen)
for (Entry m : modulesInLayer) {
if (addedModules.add(m.getKey())) {
moduleRefs.add(m);
}
}
}
return moduleRefs;
}
private static Class>[] getCallStack() {
// Try StackWalker (JDK 9+)
PrivilegedAction[]> stackWalkerAction = new PrivilegedAction[]>() {
@Override
public Class>[] run() {
List> stackFrameClasses = new ArrayList<>();
StackWalker.getInstance(Option.RETAIN_CLASS_REFERENCE)
.forEach(sf -> stackFrameClasses
.add(sf.getDeclaringClass()));
return stackFrameClasses.toArray(new Class>[0]);
}
};
try {
// Try with doPrivileged()
return AccessController
.doPrivileged(stackWalkerAction);
} catch (Exception e) {
}
try {
// Try without doPrivileged()
return stackWalkerAction.run();
} catch (Exception e) {
}
// Try SecurityManager
PrivilegedAction[]> callerResolverAction = new PrivilegedAction[]>() {
@Override
public Class>[] run() {
return new CallerResolver().getClassContext();
}
};
try {
// Try with doPrivileged()
return AccessController
.doPrivileged(callerResolverAction);
} catch (Exception e) {
}
try {
// Try without doPrivileged()
return callerResolverAction.run();
} catch (Exception e) {
}
// As a fallback, use getStackTrace() to try to get the call stack
try {
throw new Exception();
} catch (final Exception e) {
final List> classes = new ArrayList<>();
for (final StackTraceElement elt : e.getStackTrace()) {
try {
classes.add(Class.forName(elt.getClassName()));
} catch (final Throwable e2) {
// Ignore
}
}
if (classes.size() > 0) {
return classes.toArray(new Class>[0]);
} else {
// Last-ditch effort -- include just this class in the call stack
return new Class>[] { Java9Scanner.class };
}
}
}
private static boolean isSystemModule(
final ModuleReference moduleReference) {
String name = moduleReference.descriptor().name();
if (name == null) {
return false;
}
return name.startsWith("java.") || name.startsWith("jdk.")
|| name.startsWith("javafx.") || name.startsWith("oracle.");
}
public static void main(String[] args) throws Exception {
// Get ModuleReferences for modules of all classes in call stack,
List> systemModuleRefs = new ArrayList<>();
List> nonSystemModuleRefs = new ArrayList<>();
Class>[] callStack = getCallStack();
List> moduleRefs = findModuleRefs(
callStack);
// Split module refs into system and non-system modules based on module name
for (Entry m : moduleRefs) {
(isSystemModule(m.getKey()) ? systemModuleRefs
: nonSystemModuleRefs).add(m);
}
// List system modules
System.out.println("\nSYSTEM MODULES:\n");
for (Entry e : systemModuleRefs) {
ModuleReference ref = e.getKey();
System.out.println(" " + ref.descriptor().name());
}
// Show info for non-system modules
System.out.println("\nNON-SYSTEM MODULES:");
for (Entry e : nonSystemModuleRefs) {
ModuleReference ref = e.getKey();
ModuleLayer layer = e.getValue();
System.out.println("\n " + ref.descriptor().name());
System.out.println(
" Version: " + ref.descriptor().toNameAndVersion());
System.out.println(
" Packages: " + ref.descriptor().packages());
System.out.println(" ClassLoader: "
+ layer.findLoader(ref.descriptor().name()));
Optional location = ref.location();
if (location.isPresent()) {
System.out.println(" Location: " + location.get());
}
try (ModuleReader moduleReader = ref.open()) {
Stream stream = moduleReader.list();
stream.forEach(s -> System.out.println(" File: " + s));
}
}
}
}