有没有办法修改模块路径和程序化 JShell 实例的添加模块?

Is there a way to modify module path and added modules of a programmatic JShell instance?

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

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

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

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

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

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

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

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);
    }
}

building/packaging之后:

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

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

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

UPDATE - 我已经知道如何设置模块路径了:

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

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

可以使用/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

您可以在代码中的 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);

The specified path is added to the end of the classpath used in eval(). Note that the unnamed package is not accessible from the package in which eval(String) code is placed.

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


在你的情况下,我在这里猜测,虽然你这样做,mysql-connector-java-5.1.35.jar理想情况下,将被视为存在于 class 路径上的 自动模块 ,因此 class com.mysql.jdbc.Driver 将可访问。


Update :- 进一步探索我认为实现这一目标的更好方法可能是尝试使用 Jshell.Builder and its option 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);