【问题标题】:Is there a way to modify module path and added modules of a programmatic JShell instance?有没有办法修改模块路径和程序化 JShell 实例的添加模块?
【发布时间】:2018-03-06 02:28:21
【问题描述】:

我正在尝试通过我使用JShell API 创建的 JShell 实例在运行时运行一些 Java 代码。为了演示我的问题,我将分享我的简单代码。

根据我当前的设置,我有一个名为 lib 的目录,其中包含 MySQL Java 驱动程序:mysql-connector-java-5.1.35.jar

通过命令工具启动 JShell 并将所需模块添加为:

jshell --module-path lib --add-modules mysql.connector.java

然后加载 mysql 驱动程序对我有用:

jshell> Class.forName("com.mysql.jdbc.Driver").newInstance();
$1 ==> com.mysql.jdbc.Driver@42f93a98

我用module-info.java 创建了一个类似的Java 9 模块:

module example.loadmysql {
    requires java.sql;
    requires mysql.connector.java;
    requires jdk.jshell;
}

src/example/loadmysql/Runner.java 为:

package example.loadmysql;

import jdk.jshell.*;
import java.sql.*;

public class Runner {
    public static void main(String[] args) throws Exception {
        // this works because this module requires mysql.connector.java
        System.out.println(Class.forName("com.mysql.jdbc.Driver").newInstance());

        JShell js = JShell.create();
        String code = ""
            + "try {"
            + "    Class.forName(\"com.mysql.jdbc.Driver\").newInstance();"
            + "} catch (Exception e) {"
            + "    System.out.println(e.toString());"
            + "}";
        js.eval(code);
    }
}

构建/打包后:

java -p lib -m example.loadmysql
com.mysql.jdbc.Driver@6a4f787b
java.lang.ClassNotFoundException: com.mysql.jdbc.Driver

很明显,尽管 example.loadmysql 模块需要 mysql 连接器,但创建的 JShell 实例不需要。所以找不到类。

关于如何以编程方式将模块添加到 JShell 实例,使其像直接 JShell 编码示例一样工作的任何想法?

更新 - 我已经弄清楚如何设置模块路径:

String modulePath = System.getProperty("jdk.module.path");
js.eval("System.setProperty(\"jdk.module.path\", \""
    + modulePath + "\");");

但这还不够。我仍然以某种方式添加了所需的模块。

【问题讨论】:

  • 您是否尝试将addToClassPath 放在eval 之前?
  • 我可以添加到类路径中,这似乎可行。尽管它不符合 Java 9 和使用模块的精神!如果类路径方法仍然有效,我不确定mysql连接器是否最终转换为模块。如果没有其他解决方案,我可能会这样做!非常感谢。

标签: java java-9 jshell module-path


【解决方案1】:

您可以在代码中在eval 之前使用addToClassPath

JShell js = JShell.create();
js.addToClasspath("path/to/add/to/the/classpath");
String code = ""
        + "try {"
        + "    Class.forName(\"com.mysql.jdbc.Driver\").newInstance();"
        + "} catch (Exception e) {"
        + "    System.out.println(e.toString());"
        + "}";
js.eval(code);

指定的路径被添加到中使用的类路径的末尾 eval()。请注意,未命名的包无法从 放置eval(String)代码的包。

从文档看来,eval 后返回的 JShell 状态基于类路径执行代码,因此为了向其添加任何进一步的依赖项,您需要使用相同的方法将其添加到类路径中.


在你的情况下,我猜想在你这样做的时候,mysql-connector-java-5.1.35.jar 理想情况下会被视为一个自动模块 存在于类路径中,因此可以访问类 com.mysql.jdbc.Driver


更新:- 进一步探索我认为实现这一目标的更好方法可能是尝试使用 Jshell.Builder 及其选项 compilerOptions 创建一个实例默认编译选项有点像(未测试) -

JShell js = JShell.builder()
                 .compilerOptions("--module-path lib","--add-modules mysql.connector.java").build();
String code = ""
    + "try {"
    + "    Class.forName(\"com.mysql.jdbc.Driver\").newInstance();"
    + "} catch (Exception e) {"
    + "    System.out.println(e.toString());"
    + "}";
js.eval(code);

【讨论】:

  • 谢谢!这种方法现在可以使用。我只需要跟踪模块路径和类路径。我更喜欢用 Java 9 方式处理它,但如果这不可能,我可以坚持这一点。我想我现在会接受你的回答!
  • @DylanJohnson ya 似乎不是一个理想的方式,但似乎 eval 本身使用类路径来评估提供的代码。
  • @DylanJohnson 虽然没有经过测试。但也许编辑应该是一种更清洁的方式来提供帮助。
  • 好主意!不幸的是,我得到了 IllegalArgumentExceptions。我似乎也找不到关于这个 compilerOptions 方法的太多文档。比如哪些标志是合法的!不过谢谢你;这给了我更多尝试的想法。
【解决方案2】:

可以使用/env命令添加模块,查看帮助:

/env [-class-path <path>] [-module-path <path>] [-add-modules <modules>] ...
|   view or change the evaluation context

详情:

jshell> /help context
|  
|  context
|  
|  These options configure the evaluation context, they can be specified when
|  jshell is started: on the command-line, or restarted with the commands /env,
|  /reload, or /reset.
|  
|  They are:
|   --class-path <class search path of directories and zip/jar files>
|       A list of directories, JAR archives,
|       and ZIP archives to search for class files.
|       The list is separated with the path separator
|       (a : on unix/linux/mac, and ; on windows).
|   --module-path <module path>...
|       A list of directories, each directory
|       is a directory of modules.
|       The list is separated with the path separator
|       (a : on unix/linux/mac, and ; on windows).
|   --add-modules <modulename>[,<modulename>...]
|       root modules to resolve in addition to the initial module.
|       <modulename> can also be ALL-DEFAULT, ALL-SYSTEM,
|       ALL-MODULE-PATH.
|   --add-exports <module>/<package>=<target-module>(,<target-module>)*
|       updates <module> to export <package> to <target-module>,
|       regardless of module declaration.
|       <target-module> can be ALL-UNNAMED to export to all
|       unnamed modules. In jshell, if the <target-module> is not
|       specified (no =) then ALL-UNNAMED is used.
|  
|  On the command-line these options must have two dashes, e.g.: --module-path
|  On jshell commands they can have one or two dashes, e.g.: -module-path

【讨论】:

  • 我相信 OP 正在尝试使用 java 代码来实现相同的目标。否则,--module-path--add-modules 也可以正常工作,如问题中所述。
  • 不幸的是,我通过测试发现您无法以编程方式评估 /env 命令。只能直接在 JShell 内部工作。好主意!
  • @DylanJohnson 是的,这确实不起作用:(。如果我仔细阅读文档,我的印象是它应该可以工作(见更新)。没有提到JShell 不支持这个功能。需要更多调查...
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2018-08-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-09-10
  • 1970-01-01
  • 2021-07-01
相关资源
最近更新 更多