Хотя вы явно не упомянули об этом, я думаю, что вы работаете с версией Java с модулями (JDK 9+), но руководства, которым вы следовали, относятся к более ранним версиям, начиная с Java 6.Вот почему вы получаете ошибку о неподдерживаемом listLocationsForModules
, потому что разработчики JDK модифицировали FileManager
с помощью методов по умолчанию, которые выдают UnsupportedOperationException
.
Если вы на самом деле не хотите использовать версиюJava больше 8, я бы придерживался JDK8, это будет намного проще!
Я продолжу, если вы действительно хотите использовать Java 9 и выше (однако протестировал мой код в Java 11):
Для работы с модулями достаточно, чтобы ваш файловый менеджер делегировал стандартный файловый менеджер:
@Override
public Location getLocationForModule(Location location, String moduleName) throws IOException {
return standardFileManager.getLocationForModule(location, moduleName);
}
@Override
public Location getLocationForModule(Location location, JavaFileObject fo) throws IOException {
return standardFileManager.getLocationForModule(location, fo);
}
@Override
public Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException {
return standardFileManager.listLocationsForModules(location);
}
@Override
public String inferModuleName(Location location) throws IOException {
return standardFileManager.inferModuleName(location);
}
Я также счел необходимым изменить код Atamur для явной проверки базового Java-модуля (чтобы мы моглиразрешите java.lang в Java 9+!) и делегируйте стандартному файловому менеджеру, как это было бы для пути к классу платформы в предыдущих версиях:
@Override
public Iterable<JavaFileObject> list(Location location, String packageName, Set<JavaFileObject.Kind> kinds, boolean recurse) throws IOException {
boolean baseModule = location.getName().equals("SYSTEM_MODULES[java.base]");
if (baseModule || location == StandardLocation.PLATFORM_CLASS_PATH) { // **MODIFICATION CHECK FOR BASE MODULE**
return standardFileManager.list(location, packageName, kinds, recurse);
} else if (location == StandardLocation.CLASS_PATH && kinds.contains(JavaFileObject.Kind.CLASS)) {
if (packageName.startsWith("java") || packageName.startsWith("com.sun")) {
return standardFileManager.list(location, packageName, kinds, recurse);
} else { // app specific classes are here
return finder.find(packageName);
}
}
return Collections.emptyList();
}
Обновления
Некоторые другие моменты:
Извлечение встроенных пружинных классов загрузки:
Получите jarUri, выполнив поиск последнего индекса '!»в каждом packageFolderURL, как в комментарии Taeyun Kim , а не в первом, как в исходном примере.
private List<JavaFileObject> processJar(URL packageFolderURL) {
List<JavaFileObject> result = new ArrayList<JavaFileObject>();
try {
// Replace:
// String jarUri = packageFolderURL.toExternalForm().split("!")[0];
// With:
String externalForm = packageFolderURL.toExternalForm();
String jarUri = externalForm.substring(0, externalForm.lastIndexOf('!'));
JarURLConnection jarConn = (JarURLConnection) packageFolderURL.openConnection();
String rootEntryName = jarConn.getEntryName();
int rootEnd = rootEntryName.length()+1;
// ...
Это позволяет пакету PackageInternalsFinder
возвращать CustomJavaFileObject
с полными URI для классов ввстроенные пружинные банки (ниже BOOT-INF/lib
), которые затем разрешаются с помощью обработчика URI банок с пружинной загрузкой , который регистрируется аналогично объяснению в этом ответе .Обработка URI должна происходить автоматически через загрузочную пружину.