3

我正在尝试从给定的 WAR 文件中动态提取配置。我的目标是找到所有实现某个接口(Parameter)的类。

war 文件不在类路径上,所以我创建了一个临时类加载器来检查它的类。

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我在我的 webapp 中找到了所有的实现。

现在,我想对WEB-INF/lib. 不幸的是,在这种情况下,我还没有找到构建类加载器的方法。类加载器似乎忽略了jar URL ( jar:<url>!/WEB-INF/lib/{entry}.jar)。

当我运行下面的代码时, 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");

如果可能的话,我想避免WEB-INF/lib/*.jar从 WAR 文件中提取并单独分析它们。

我在这里遗漏了什么?(其他方法,创建类加载器的其他方法,......任何线索都会有所帮助)。也许这可以在不使用 Reflections 库的情况下完成?

4

1 回答 1

2

这当然是可能的。这是一个丑陋的例子:

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");

问题:

  1. 需要一种优雅的万无一失的方法将类名转换为 JAR 中的文件路径。就我而言"jlaj.NewJFrame" -> "jlaj/NewJFrame.class",但是在尝试加载内部类时会变得非常难看,也许在我什至无法想到的其他情况下。
  2. 读取类数据可能应该属于一些实用readAll方法,因为它看起来很乱。
  3. 显然,这需要一个非匿名类,同时将 WAR 名称和内部 JAR 名称传递给构造函数。或者ZipFileand ZipEntry,如果你想在外部迭代它们。

从积极的方面来说,它是有效的。

于 2015-12-13T06:29:23.170 回答