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でのすべての実装を見つけます。

ここで、 に存在する各 jar に対して同じことを行いたいと思います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 名の両方を持つ非匿名クラスが必要です。またはZipFileZipEntryそれらを外部で反復処理する場合。

良い面では、それは機能します。

于 2015-12-13T06:29:23.170 に答える