簡體   English   中英

如何從 .war 文件加載類並使用它們?

[英]How to load classes from a .war file and use them?

我有一個項目正在生成一個.war文件作為構建工件。 我想將此.war加載到 JVM 中,以便解析它包含的運行時注釋,但在加載類時遇到問題。

我有以下代碼片段從JarFile獲取類名:

public static Set<String> getClassNamesFromJarFile(JarFile jarFile) throws IOException {
    Set<String> classNames = new HashSet<>();
    Enumeration<JarEntry> e = jarFile.entries();
    while (e.hasMoreElements()) {
        JarEntry jarEntry = e.nextElement();
        if (jarEntry.getName().endsWith(".class")) {
            String className = jarEntry.getName()
                    .replace("/", ".");
            className = className.substring(0, className.length() - 6);
            classNames.add(className);
        }
    }
    return classNames;
}

我使用這個片段來獲取JarFile

protected static JarFile getJarFile(String jarFileUrl) throws IOException {
    try {
        return new JarFile(new URI(jarFileUrl.replaceAll(" ", "%20")).getSchemeSpecificPart());
    } catch (URISyntaxException ex) {
        return new JarFile(jarFileUrl.substring("file:".length()));
    }
}

然后,在我的主要部分,我有以下代碼:

public static void main(String[] args) throws IOException {

    String warpath = "C:\\User\\Me\\Desktop\\someWarFile.war";

    // Fetch JarFile
    JarFile jar = getJarFile("file:" + warpath);

    // Load .war into the JVM
    URL url = new File(warpath).toURI().toURL();
    URLClassLoader child = new URLClassLoader(
            new URL[]{url},
            Object.class.getClassLoader()
    );

    // Get class names in the .war
    Set<String> classes = getClassNamesFromJarFile(jar);

    // Try to get classes from name
    for (String className : classes) {
        try {
            System.out.println("CLASS FOUND: " + className);
            Class<?> actualClass= Class.forName(className, true, child);
        } catch(Error | Exception e) {
            System.out.println("ERROR: " + e);
        }
    }
}

對於每個類,我都會得到輸出:

...
CLASS FOUND: WEB-INF.classes.com.some.company.project.state.ProjectStateProvider$1
ERROR: java.lang.NoClassDefFoundError: com/some/company/project/state/ProjectStateProvider$1 (wrong name: WEB-INF/classes/com/some/company/project/state/ProjectStateProvider$1)
CLASS FOUND: WEB-INF.classes.com.some.company.configuration.rest.JsonProvider
ERROR: java.lang.NoClassDefFoundError: com/some/company/configuration/rest/JsonProvider (wrong name: WEB-INF/classes/com/some/company/configuration/rest/JsonProvider)
...

我試過刪除WEB-INF. WEB-INF.classes. 來自類名的前綴,我也嘗試將.class添加到類名和以前解決方案的每個組合中,但我只得到一個java.lang.ClassNotFoundException

如何引用從.war加載的類,例如WEB-INF.classes.com.some.company.project.state.ProjectStateProvider$1類?

我現在更新了代碼。 它現在在 .war 文件中找到相應的類文件。

現在會出現的問題是,如果要加載一個依賴於其他尚未加載的類的類。

所以你可以

  • 在那里中斷加載並搜索該文件(編寫一個自定義的 ClassLoader,它首先掃描所有 jar/war 文件以查找 .class 文件而不加載它們)
  • 按照 .war 文件中給出的加載順序(不知道在哪里可以找到那個,壽

代碼:

package stackoverflow;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;

public class JarLoading {



    public static void main(final String[] args) throws MalformedURLException, IOException {
        final String fileName = "D:\\upload\\K4D-Prozesstafel.war";
        final File file = new File(fileName);
        reloadWar(file);
    }



    static private void reloadWar(final File pJarFile) throws MalformedURLException, IOException {
        System.out.println("JarLoading.reloadJar() " + pJarFile);
        final String defaultPattern = "jar:file:" + pJarFile + "!/WEB-INF/classes/";
        //      final URL oldURL = pJarFile.toURI().toURL();
        final URL newURL = new URL(defaultPattern);
        System.out.println("DP:\t" + defaultPattern);

        try (final URLClassLoader classLoader = new URLClassLoader(new URL[] { newURL }); //
                final JarFile file = new JarFile(pJarFile);) {

            final Enumeration<JarEntry> entries = file.entries();
            while (entries.hasMoreElements()) {
                final JarEntry jarEntry = entries.nextElement();
                if (!isClassFile(jarEntry)) {
                    continue;
                }

                try {
                    final String id = getClassName(jarEntry);
                    System.out.println("Loading file " + id);
                    final Class<?> cls = classLoader.loadClass(id);
                    System.out.println("\tLoaded file: " + cls);
                } catch (final ClassNotFoundException e2) {
                    System.out.println("\tFile not a class file: " + jarEntry.getName() + "\n\t\t" + e2);
                    return;
                }
            }

            //          if (plugin.isValid()) mPlugins.add(plugin);
        }
    }



    static private boolean isClassFile(final ZipEntry entry) {
        return !entry.isDirectory() && entry.getName().endsWith(".class");
    }
    static private String getClassName(final ZipEntry entry) {
        String className = entry.getName().replace('/', '.'); // including ".class"
        className = className.substring(0, className.length() - ".class".length());
        className = className.replace("WEB-INF.classes.", "");
        return className;
    }
}

現在這可能會導致異常,例如:

JarLoading.reloadJar() D:\upload\K4D-Prozesstafel.war
DP: jar:file:D:\upload\K4D-Prozesstafel.war!/WEB-INF/classes/
Loading file biz.k4d.gremienplaner.prozesstafel.cruds.BoxCRUD
Exception in thread "main" java.lang.NoClassDefFoundError: biz/cbsoft/lib/jee/glassfish/io/crud/TemplateCRUD_BaseEntity

因為類文件biz.k4d.gremienplaner.prozesstafel.cruds.BoxCRUD現在已加載,但它依賴於另一個 jar 中的文件,即biz/cbsoft/lib/jee/glassfish/io/crud/TemplateCRUD_BaseEntity

好的。 我稍微改變了getClassNamesFromJarFile方法:

public static Set<String> getClassNamesFromJarFile(
        JarFile file, String pfx, String sfx) {
    Set<String> result = new HashSet<>();
    Enumeration<JarEntry> enm = file.entries();
    while (enm.hasMoreElements()) {
        JarEntry e = enm.nextElement();
        String path = e.getName();
        if (path.startsWith(pfx) && path.endsWith(sfx)) {
            path = path.substring(pfx.length(), path.length()-sfx.length());
            path = path.replace('/', '.');
            result.add(path);
        }
    }
    return result;
}

然后:

public static void main(String[] args) throws IOException {

    String warpath = "C:\\User\\Me\\Desktop\\someWarFile.war";

    // Fetch JarFile
    JarFile jar = new JarFile(warpath);

    // Load .war into the JVM
    URL url = new URL("jar:file:" + warpath + "!/WEB-INF/classes/");
    URLClassLoader child = new URLClassLoader(
            new URL[]{url},
            Object.class.getClassLoader()
    );

    // Get class names in the .war
    Set<String> classes = getClassNamesFromJarFile(jar, "WEB-INF/classes/", ".class");

    // Try to get classes from name
    for (String className : classes) {
        try {
            System.out.println("CLASS FOUND: " + className);
            Class<?> actualClass= Class.forName(className, true, child);
        } catch(Error | Exception e) {
            System.out.println("ERROR: " + e);
        }
    }
}

更新:

現在,如果類依賴於不在類路徑中的其他類,它們將不會加載。 例如,如果 war 包含 WEB-INF/lib 中的 jar 文件,則需要將它們添加到URLClassLoader中。 您還應該提供 servlet API 的 jar。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM