【问题标题】:How to run a java class from a jar file dynamically如何从 jar 文件中动态运行 java 类
【发布时间】:2015-05-30 12:59:15
【问题描述】:

我正在处理一个需要作为服务器运行的第三方 java 程序才能工作的 java 项目。

通常,我会这样做:

java -cp jarfile1.jar:jarfile2.jar className arg1 arg2

然后我会运行我的 java 代码。这样就可以了。

我想知道是否有任何方法(包括我的项目所需的两个 .jar)直接从我的代码运行该类,而不必手动启动它。

正如我在一些示例中看到的那样,我尝试使用 URLClassLoader,但要么我做错了,要么没有涵盖此特定用例。

URLClassLoader classLoader = URLClassLoader.newInstance(new URL[]{new URL("file:///tmp/jarfile1.jar"),new URL("file:///tmp/jarfile2.jar")});
Class<?> cls = classLoader.loadClass("className");
Method method = cls.getDeclaredMethod ("main");
Object instance = cls.newInstance();
Object result = method.invoke (instance);

产量

Exception in thread "main" java.lang.NoClassDefFoundError: alice/tuprolog/lib/InvalidObjectIdException
at java.lang.Class.getDeclaredMethods0(Native Method)
at java.lang.Class.privateGetDeclaredMethods(Class.java:2615)
at java.lang.Class.getDeclaredMethod(Class.java:2007)
at pkg1.MainClass.main(MainClass.java:54)
Caused by: java.lang.ClassNotFoundException: alice.tuprolog.lib.InvalidObjectIdException
    at java.net.URLClassLoader$1.run(URLClassLoader.java:366)
    at java.net.URLClassLoader$1.run(URLClassLoader.java:355)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:354)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:425)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:308)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:358)
    ... 4 more

请注意,我将 .jar 复制到 /tmp 以隔离失败原因。这些文件存在并且可以访问。

我怎样才能让它在java代码中运行上面指定的类?

谢谢!

【问题讨论】:

  • 对不起,我不关注。将代码保存为可运行的 .jar 文件如何使我免于运行第三方类?能详细点吗?
  • ClassName.main("arg1", "arg2");?
  • 向我们展示带有URLClassLoader 的代码以及您遇到的错误
  • 哦,很好。这会在一个单独的线程上工作吗(所以主线程不会被阻塞)?如果是这样,请发布答案,以便我接受:)
  • 不,它不会在单独的线程中执行。但是没有什么能阻止你启动一个调用它的线程。如果包含 ClassName 的 jar 文件位于类路径中,则不需要单独的类加载器(除非您特别想要隔离)

标签: java jar classloader


【解决方案1】:

如果该类存在于不同的 ClassLoader 中,则需要使用反射来获取它:

ClassLoader classLoader = new URLClassLoader(
    new URL[] { firstJarURL, secondJarURL });

String[] args = { arg1, arg2 };

try {
    Class<?> mainClass = classLoader.loadClass("com.somepackage.ClassName");
    mainClass.getMethod("main", String[].class).invoke(null, args);
} catch (ReflectiveOperationException e) {
    throw new RuntimeException(e);
}

【讨论】:

  • 恐怕它不起作用,它产生了与我问题中的编辑相同的错误:(
  • 您是否验证过“alice/tuprolog/lib/InvalidObjectIdException.class”存在于其中一个 jar 文件中?
  • 这至少解决了对main的正确调用。但是首先尝试找到具有类似代码的类alice.tuprolog.lib.InvalidObjectIdException。首先用罐子里的拉链工具看看。您还可以将当前类加载器 / ClassLoader.getSystemClassLoader() 作为第二个参数添加到 URLClassLoader。
  • 是的,在第二个
【解决方案2】:

我终于修好了!完成的事情:

  1. 我忘记将第二个 jar 添加到项目的类路径中(呃!)
  2. 由于一切都在项目类路径中,我只是重用了当前的类加载器

最终工作代码:

ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Class<?> cls = classLoader.loadClass("className");
Method method = cls.getDeclaredMethod("main", String[].class);
Object instance = cls.newInstance();
Object result = method.invoke(null, (Object)args);

感谢大家,特别感谢 cmets 中的 VGR 和 Joop Eggen 指出第二个 jar 的错误!

编辑:正如 JB Nizet 在 cmets 中指出的那样,直接调用类的 main() 方法更简单:

className.main(args);

你已经完成了

【讨论】:

  • 既然类名在Java代码中是硬编码的,为什么还要使用反射。为什么不简单地将您的 5 行替换为 className.main(args)?此外,您不应创建 className 的实例来调用 main()。 main() 是一个静态方法。将 null 作为 method.invoke() 的第一个参数传递。
  • 没错,我会把它添加到答案中
猜你喜欢
  • 2013-07-04
  • 2013-07-15
  • 2017-11-21
  • 2020-06-08
  • 2014-06-07
  • 2017-08-18
  • 1970-01-01
  • 2012-09-29
  • 2018-01-03
相关资源
最近更新 更多