Java 9 webstart JNLP服务产生IllegalAccess

Java 9 webstart JNLP Service produces IllegalAccess

以下代码(通过 JNLP API 检索 Java Web Start 客户端应用程序的基础 URL)在 Java 8 中有效但在执行时失败在(模块化)Java 9 运行时:

Class<?> mclass = Class.forName("javax.jnlp.ServiceManager");
Method lookup = mclass.getMethod("lookup", new Class[]{String.class});
Object basicSvc = lookup.invoke(null, new Object[{"javax.jnlp.BasicService"});
Class<?> sclass = basicSvc.getClass();
Method getCodeBase = sclass.getMethod("getCodeBase", (Class[])null);
URL codebase = (URL)getCodeBase.invoke(basicSvc, (Object[])null); // throws

结果

java.lang.IllegalAccessException: class app.App cannot access class
  com.sun.jnlp.BasicServiceImpl (in module jdk.javaws) because module
  jdk.javaws does not export com.sun.jnlp to unnamed module @7202a0fa
    at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException
    at java.base/java.lang.reflect.AccessibleObject.checkAccess
    at java.base/java.lang.reflect.Method.invoke
    at app.App.init

如何解决这个问题?

正如前面讨论的更一般question问题是第二种反射方法不是由publicAPIclass定义的,而是私有实现,这不起作用,因为 Java 9 个可访问性规则适用。

解决方法是将 getCodeBase 方法改为基于 public 接口:

Class<?> sclass = Class.forName("javax.jnlp.BasicService");

这也避免了反射反模式与动态定义 classes 一起工作。

使用静态实现也可以避免这个问题(但是这有一个问题,它需要 javaws.jar,这在某些构建环境中可能不容易获得)。

import javax.jnlp.BasicService;
import javax.jnlp.ServiceManager;

BasicService basicSvc = (BasicService)ServiceManager.lookup("javax.jnlp.BasicService");
URL u = basicSvc.getCodeBase();

感谢 @Holger to check the reflection implementation and @Alan Bateman in guessing what the actual problem was without seeing the code. Seperated the two questions as suggested by @nicolai,它变得更干净了。