【问题标题】:How do I find the correct *.class file to run Java application?如何找到正确的 *.class 文件来运行 Java 应用程序?
【发布时间】:2016-11-11 11:34:39
【问题描述】:

考虑一个场景,我得到了一个我一无所知的 Java 项目,我必须编译并运行它

我想要一些解决方案,让我能够编译和运行任何程序尽可能快地不了解项目

我尝试了什么:

为此,我使用以下过程来编译程序。

# compile *.java files into *.class files
javac -d . -encoding UTF-8 *.java

要运行我正在使用以下命令的程序,但它需要 package 和 class 方法 main 所在的位置。我不知道。

java -Dfile.encoding=utf-8 package.class

我发现可以使用循环和javap来检查它,所以我写了一个简单的shell脚本来自动编译程序,找到main方法所在的包和类并运行它:

containsMain()
{
    if javap -public $1 | fgrep 'public static void main(' > /dev/null; then
        echo $1 | sed 's/^.\///' | cut -f 1 -d '.' | tr / .
    fi
}

for classfile in */*.class; do
    runclass=$(containsMain $classfile);
    if [ "$runclass" ]; then
        break
    fi
done

java -Dfile.encoding=utf-8 $runclass

我发现了什么问题:

这按预期工作,但 javap 地狱。 检查每个文件需要 1.2 秒。因此,在最坏的情况下,它必须遍历所有文件才能找到 main 方法,这可能需要很长时间。

另一种解决方案?

我正在考虑编写一些正则表达式来在 *.java 文件中搜索方法 main解析其所在的类和包。这并不像看起来那么容易,因为 ma​​in 方法可以用多种方式编写(修饰符)。它可以写成cmets、字符串等。甚至可以有文件中有多个类

但如果这是一个正确的解决方案,我有点害怕,我想知道在我开始做这样的事情之前没有其他方法。

感谢您的建议:)

【问题讨论】:

    标签: java linux performance compilation debian


    【解决方案1】:

    如果你可以将你的 java 应用打包到 jar 中,那么很容易从 Manifest 文件中找到主类。

        JarFile j = new JarFile(new File("jarfile.jar"));
        String mainClassName = j.getManifest().getMainAttributes().getValue("Main-Class");
    

    【讨论】:

    • 要将应用程序捆绑到 *.jar 中,我首先需要知道生成该 *.jar 文件的主要方法所在的包和类。如果我有 *.jar 文件,我会直接运行它而不需要其他任何东西。
    【解决方案2】:

    如果有一个主方法,您可以通过放置要编译的源的启动器主类(例如,在默认包中)然后启动该启动器主类来代替。 然后,此启动器 Main 类可以检查 ClassLoader 以查找所有类,并通过反射查找其中一个类是否具有 main 方法。示例代码可能如下所示:

    public class Main {
    
    private static boolean found = false;
    
    public static void main(String[] args) {
        try {
            ClassLoader classLoader = Main.class.getClassLoader();
            if (classLoader instanceof URLClassLoader) {
                URLClassLoader urlClassLoader = (URLClassLoader) classLoader;
                URL[] urLs = urlClassLoader.getURLs();
                for (URL url : urLs) {
                    File classesDir = new File(url.toURI());
                    Path classesPath = classesDir.toPath();
                    int lenOfStartPath = classesDir.getAbsolutePath().length();
                    Files.walkFileTree(classesPath, new SimpleFileVisitor<Path>() {
                        @Override
                        public FileVisitResult visitFile(Path path, BasicFileAttributes attrs)
                                throws IOException {
                            File file = path.toFile();
                            if (file.getName().endsWith(".class")) {
                                String className = file.getAbsolutePath().substring(lenOfStartPath + 1);
                                className = className.substring(0,className.length() - ".class".length());
                                className = className.replace('\\', '.');
                                className = className.replace('/','.');
                                if (!Main.class.getName().equals(className)) {
                                    try {
                                        Class<?> potentialMainClass = Class.forName(className);
                                        Method mainMethod = potentialMainClass.getMethod("main", new Class[] {String[].class});
                                        found = true;
                                        try {
                                            mainMethod.invoke(null, (Object) args);
                                        } catch (Exception e) {
                                            e.printStackTrace();
                                        }
                                        return FileVisitResult.TERMINATE;
                                    } catch (NoSuchMethodException|ClassNotFoundException e) {
                                        //Class not found of has no main method.
                                    }
                                }
                            }
                            return FileVisitResult.CONTINUE;
                        }
                    });
                }
            }
            if (!found)
                System.err.println("No proper class found");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    }
    

    当然,你可能不知道应该将什么参数传递给 main 方法,所以它也只适用于没有参数的主类。

    【讨论】:

      【解决方案3】:

      考虑一个场景,我得到了一个我一无所知的 Java 项目,我必须编译并运行它。

      没有(单一)解决方案,因为可能有多个 main 方法,这是有道理的(想象一下 Zip.java 和 Unzip.java)。

      我正在考虑编写一些正则表达式来搜索 *.java 文件中的方法 main 并解析它所在的类和包。

      作为第一个过滤器,我会用 grep 搜索 main ("\bmain\b") 这个词。仅此一项就足以让 javap 接受 1.2 秒。


      您还可以添加自己的包含主类的类,该类尝试反射性地调用所有其他类的main 方法,直到成功。这会加载所有类,这需要一些时间,但肯定比 javap 少。

      【讨论】:

      • 好点与多个main 方法。实际上我想在 2 秒内编译和运行 Java 程序。编译已经花费了 2.4 秒。所以我不想运行javap 程序,因为它需要很长时间。我想知道是否真的没有办法在不搜索要运行的文件的情况下轻松运行应用程序。
      猜你喜欢
      • 1970-01-01
      • 2011-08-11
      • 1970-01-01
      • 2021-07-09
      • 2012-10-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多