Java 9 webstart JNLP Service生成IllegalAccess

eck*_*kes 1 java reflection jnlp java-platform-module-system java-9

以下代码(通过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
Run Code Online (Sandbox Code Playgroud)

结果是

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
Run Code Online (Sandbox Code Playgroud)

怎么解决这个问题?

eck*_*kes 5

正如之前更普遍的问题所讨论的那样,问题在于第二个反射方法不是由公共API类定义的,而是私有实现,由于Java 9可访问性规则适用,因此不起作用.

解决方法是将getCodeBase方法基于公共接口:

Class<?> sclass = Class.forName("javax.jnlp.BasicService");
Run Code Online (Sandbox Code Playgroud)

这也避免了反射反模式与动态定义类一起使用.

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

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

BasicService basicSvc = (BasicService)ServiceManager.lookup("javax.jnlp.BasicService");
URL u = basicSvc.getCodeBase();
Run Code Online (Sandbox Code Playgroud)

感谢@Holger检查反射实现,@ Alan Bateman在没有看到代码的情况下猜测实际问题是什么.按照@nicolai的建议分离了两个问题,这使得它更清洁.