从 WAR 文件中的 JAR 构建类路径
Build classpath from JAR inside a WAR file
我正在尝试从给定的 WAR 文件中动态提取配置。
我的目标是找到实现某些接口的所有 class (Parameter
)。
war 文件不在 class 路径中,因此我创建了一个临时 classloader 来检查它的 classes。
URL webClasses = new URL("jar","","file:" + new File(path).getAbsolutePath() + "!/WEB-INF/classes/");
URLClassLoader cl = URLClassLoader.newInstance(new URL[] { webClasses });
Reflections reflections = new Reflections(ClasspathHelper.forClassLoader(cl), new SubTypesScanner(), cl);
Set<Class<? extends Parameter>> params = reflections.getSubTypesOf(Parameter.class);
这很好用。我在我的网络应用程序中找到了 Parameter
的所有实现。
现在,我想对 WEB-INF/lib
中存在的每个 jar 执行相同的操作。
不幸的是,在这种情况下,我还没有找到构建 classloader 的方法。 classloader 似乎忽略了 jar URLs (jar:<url>!/WEB-INF/lib/{entry}.jar
).
当我运行下面的代码时,没有class被Reflections找到:
Set<URL> libs = findLibJars(warUrl));
// The URLs are in the following format : {myWar}.war!/WEB-INF/lib/{myLib}.jar
URLClassLoader cl = URLClassLoader.newInstance(urls.toArray(new URL[urls.size()]));
cl.loadClass("my.company.config.AppParameter");
如果可能,我想避免从 WAR 文件中提取 WEB-INF/lib/*.jar
并单独分析它们。
我在这里遗漏了什么? (其他方法,其他创建 classloader 的方法,...任何线索都会有所帮助)。也许这可以在不使用 Reflections 库的情况下完成?
当然可以。这是一个丑陋的例子:
String warName = "wlaj.war";
ClassLoader loader = new ClassLoader() {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// This probably needs fixing:
String fileName = name.replace('.', '/') + ".class";
try {
try (ZipFile zf = new ZipFile(warName)) {
ZipEntry jar = zf.getEntry("WEB-INF/lib/jlaj.jar");
if (jar == null)
throw new ClassNotFoundException("No jlaj.jar");
try (ZipInputStream jarInput = new ZipInputStream(zf.getInputStream(jar))) {
for (ZipEntry cl; (cl = jarInput.getNextEntry()) != null; ) {
if (fileName.equals(cl.getName())) {
ByteArrayOutputStream data = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
for (int len; (len = jarInput.read(buffer)) != -1; ) {
data.write(buffer, 0, len);
}
buffer = data.toByteArray();
return defineClass(name, buffer, 0, buffer.length);
}
}
}
}
throw new ClassNotFoundException();
} catch (IOException ex) {
throw new ClassNotFoundException("Error opening class file", ex);
}
}
};
loader.loadClass("jlaj.NewJFrame");
问题:
- 需要一种优雅的万无一失的方法来将 class 名称转换为 JAR 内的文件路径。在我的情况下
"jlaj.NewJFrame" -> "jlaj/NewJFrame.class"
,但是当尝试加载内部 classes 时它变得非常丑陋,也许在其他一些我什至无法考虑的情况下。
- 读取class数据应该是一些实用的
readAll
方法,因为看起来很乱。
- 显然,这需要一个非匿名 class,同时将 WAR 名称和内部 JAR 名称传递给构造函数。或者
ZipFile
和 ZipEntry
,如果你想在外部迭代它们。
从积极的方面来说,它是有效的。
我正在尝试从给定的 WAR 文件中动态提取配置。
我的目标是找到实现某些接口的所有 class (Parameter
)。
war 文件不在 class 路径中,因此我创建了一个临时 classloader 来检查它的 classes。
URL webClasses = new URL("jar","","file:" + new File(path).getAbsolutePath() + "!/WEB-INF/classes/");
URLClassLoader cl = URLClassLoader.newInstance(new URL[] { webClasses });
Reflections reflections = new Reflections(ClasspathHelper.forClassLoader(cl), new SubTypesScanner(), cl);
Set<Class<? extends Parameter>> params = reflections.getSubTypesOf(Parameter.class);
这很好用。我在我的网络应用程序中找到了 Parameter
的所有实现。
现在,我想对 WEB-INF/lib
中存在的每个 jar 执行相同的操作。
不幸的是,在这种情况下,我还没有找到构建 classloader 的方法。 classloader 似乎忽略了 jar URLs (jar:<url>!/WEB-INF/lib/{entry}.jar
).
当我运行下面的代码时,没有class被Reflections找到:
Set<URL> libs = findLibJars(warUrl));
// The URLs are in the following format : {myWar}.war!/WEB-INF/lib/{myLib}.jar
URLClassLoader cl = URLClassLoader.newInstance(urls.toArray(new URL[urls.size()]));
cl.loadClass("my.company.config.AppParameter");
如果可能,我想避免从 WAR 文件中提取 WEB-INF/lib/*.jar
并单独分析它们。
我在这里遗漏了什么? (其他方法,其他创建 classloader 的方法,...任何线索都会有所帮助)。也许这可以在不使用 Reflections 库的情况下完成?
当然可以。这是一个丑陋的例子:
String warName = "wlaj.war";
ClassLoader loader = new ClassLoader() {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
// This probably needs fixing:
String fileName = name.replace('.', '/') + ".class";
try {
try (ZipFile zf = new ZipFile(warName)) {
ZipEntry jar = zf.getEntry("WEB-INF/lib/jlaj.jar");
if (jar == null)
throw new ClassNotFoundException("No jlaj.jar");
try (ZipInputStream jarInput = new ZipInputStream(zf.getInputStream(jar))) {
for (ZipEntry cl; (cl = jarInput.getNextEntry()) != null; ) {
if (fileName.equals(cl.getName())) {
ByteArrayOutputStream data = new ByteArrayOutputStream();
byte[] buffer = new byte[4096];
for (int len; (len = jarInput.read(buffer)) != -1; ) {
data.write(buffer, 0, len);
}
buffer = data.toByteArray();
return defineClass(name, buffer, 0, buffer.length);
}
}
}
}
throw new ClassNotFoundException();
} catch (IOException ex) {
throw new ClassNotFoundException("Error opening class file", ex);
}
}
};
loader.loadClass("jlaj.NewJFrame");
问题:
- 需要一种优雅的万无一失的方法来将 class 名称转换为 JAR 内的文件路径。在我的情况下
"jlaj.NewJFrame" -> "jlaj/NewJFrame.class"
,但是当尝试加载内部 classes 时它变得非常丑陋,也许在其他一些我什至无法考虑的情况下。 - 读取class数据应该是一些实用的
readAll
方法,因为看起来很乱。 - 显然,这需要一个非匿名 class,同时将 WAR 名称和内部 JAR 名称传递给构造函数。或者
ZipFile
和ZipEntry
,如果你想在外部迭代它们。
从积极的方面来说,它是有效的。