具有依赖注入的Java模块化
让我们假设一个基于OpenJDK 11+OpenJFX 11的应用程序。代码库组织在单个java模块中(请参阅以获取参考) 到目前为止,我将所有基于JavaFX的项目与谷歌的依赖注入框架Guice相结合。对于模块化项目,这似乎不起作用 SQLiteCompassApplication.java具有依赖注入的Java模块化,java,javafx,dependency-injection,java-9,java-module,Java,Javafx,Dependency Injection,Java 9,Java Module,让我们假设一个基于OpenJDK 11+OpenJFX 11的应用程序。代码库组织在单个java模块中(请参阅以获取参考) 到目前为止,我将所有基于JavaFX的项目与谷歌的依赖注入框架Guice相结合。对于模块化项目,这似乎不起作用 SQLiteCompassApplication.java public class SQLiteCompassApplication extends Application { public static void main(String[] args
public class SQLiteCompassApplication extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage stage) throws Exception {
Injector injector = Guice.createInjector(new MyModule());
FXMLLoader fxmlLoader = injector.getInstance(FXMLLoader.class);
try(InputStream fxmlStream = getClass().getResourceAsStream("/view/View.fxml")) {
Parent root = fxmlLoader.load(fxmlStream);
stage.setScene(new Scene(root));
stage.setTitle("SQLite Compass");
stage.setOnCloseRequest(event -> System.exit(0));
stage.show();
}
}
}
module org.x1c1b.sqlitecompass {
requires javafx.controls;
requires javafx.fxml;
requires com.google.guice;
exports org.x1c1b.sqlitecompass;
}
模块信息.java
public class SQLiteCompassApplication extends Application {
public static void main(String[] args) {
launch(args);
}
public void start(Stage stage) throws Exception {
Injector injector = Guice.createInjector(new MyModule());
FXMLLoader fxmlLoader = injector.getInstance(FXMLLoader.class);
try(InputStream fxmlStream = getClass().getResourceAsStream("/view/View.fxml")) {
Parent root = fxmlLoader.load(fxmlStream);
stage.setScene(new Scene(root));
stage.setTitle("SQLite Compass");
stage.setOnCloseRequest(event -> System.exit(0));
stage.show();
}
}
}
module org.x1c1b.sqlitecompass {
requires javafx.controls;
requires javafx.fxml;
requires com.google.guice;
exports org.x1c1b.sqlitecompass;
}
为了保持简单,Guice配置(这意味着声明的Guice模块(不与java模块混淆)没有粘贴,但仍然适用于非模块化项目
执行此代码时,我收到以下异常:
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make field com.google.inject.Injector org.x1c1b.sqlitecompass.config.provider.FXMLLoaderProvider.injector accessible: module org.x1c1b.sqlitecompass does not "opens org.x1c1b.sqlitecompass.config.provider" to module com.google.guice
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:340)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:280)
at java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:176)
at java.base/java.lang.reflect.Field.setAccessible(Field.java:170)
at com.google.guice@4.2.2/com.google.inject.internal.SingleFieldInjector.<init>(SingleFieldInjector.java:38)
at com.google.guice@4.2.2/com.google.inject.internal.MembersInjectorStore.getInjectors(MembersInjectorStore.java:126)
at com.google.guice@4.2.2/com.google.inject.internal.MembersInjectorStore.createWithListeners(MembersInjectorStore.java:93)
at com.google.guice@4.2.2/com.google.inject.internal.MembersInjectorStore.access$000(MembersInjectorStore.java:36)
at com.google.guice@4.2.2/com.google.inject.internal.MembersInjectorStore$1.create(MembersInjectorStore.java:45)
at com.google.guice@4.2.2/com.google.inject.internal.MembersInjectorStore$1.create(MembersInjectorStore.java:41)
at com.google.guice@4.2.2/com.google.inject.internal.FailableCache$1.load(FailableCache.java:40)
at com.google.common@25.1-android/com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3444)
at com.google.common@25.1-android/com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2193)
at com.google.common@25.1-android/com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2152)
at com.google.common@25.1-android/com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2042)
... 39 more
Exception running application org.x1c1b.sqlitecompass.SQLiteCompassApplication
原因:java.lang.reflect.InAccessibleObject异常:无法将字段com.google.inject.Injector org.x1c1b.sqlitecompass.config.provider.FXMLLoaderProvider.Injector可访问:模块org.x1c1b.sqlitecompass不会“打开org.x1c1b.sqlitecompass.config.provider”到模块com.google.guice
位于java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:340)
位于java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:280)
位于java.base/java.lang.reflect.Field.checkCanSetAccessible(Field.java:176)
位于java.base/java.lang.reflect.Field.setAccessible(Field.java:170)
在com.google上。guice@4.2.2/SingleFieldInjector.java:38)
在com.google上。guice@4.2.2/com.google.inject.internal.MembersInjectorStore.getInjectors(MembersInjectorStore.java:126)
在com.google上。guice@4.2.2/com.google.inject.internal.MembersInjectorStore.createWithListeners(MembersInjectorStore.java:93)
在com.google上。guice@4.2.2/com.google.inject.internal.MembersInjectorStore.access$000(MembersInjectorStore.java:36)
在com.google上。guice@4.2.2/com.google.inject.internal.MembersInjectorStore$1.create(MembersInjectorStore.java:45)
在com.google上。guice@4.2.2/com.google.inject.internal.MembersInjectorStore$1.create(MembersInjectorStore.java:41)
在com.google上。guice@4.2.2/com.google.inject.internal.FailableCache$1.load(FailableCache.java:40)
在com.google上。common@25.1-android/com.google.common.cache.LocalCache$LoadingValueReference.loadFuture(LocalCache.java:3444)
在com.google上。common@25.1-android/com.google.common.cache.LocalCache$Segment.loadSync(LocalCache.java:2193)
在com.google上。common@25.1-android/com.google.common.cache.LocalCache$Segment.lockedGetOrLoad(LocalCache.java:2152)
在com.google上。common@25.1-android/com.google.common.cache.LocalCache$Segment.get(LocalCache.java:2042)
... 39多
运行应用程序org.x1c1b.sqlitecompass.SQLiteCompassApplication时出现异常
我可以通过粘贴opens org.x1c1b.sqlitecompass.config.provider来解决这个问题代码>至模块信息.java
。但这会导致更多的例外
我应该如何配置我的module/module info.java
,以便将其与Google Guice一起使用?是否可以结合使用GoogleGuice和模块化
编辑
当我更改以下行时,它似乎起作用:
- 将声明my Guice模块的org.x1c1b.sqlitecompass.config添加到
module info.java
- 使用附加VM选项执行程序
--add打开java.base/java.lang=com.google.guice
但我猜这与其说是一个正式的解决方案,不如说是一个解决方案……您必须打开所有的包,这些包将通过Guice的反射进行访问。
最简单的方法是将整个模块声明为open
,方法是在module信息中的module
前面加上这个词。。。但如果我这样做,我会收到类似的错误module java.base没有“打开java.lang”
打开完整的模块在证明访问完整的模块包及其成员方面有轻微的风险。@0x1C1B不幸的是,我认为只有这样才能“修复”您自己的错误是将java.base/java.lang=com.google.guice
作为VM参数传递。对于JDK中Guice试图以反射方式访问但无法访问的每个包,您都必须这样做。如果您发现自己正在向Guice打开java.base/java.lang,那么就有问题了。这是因为它试图侵入非公共的ClassLoader.defineClass方法吗?希望Guice会转而使用Lookup.defineClass,因为这是唯一受支持的将类注入现有运行时包的方法。但这会导致更多的异常。。和上面提到的类似吗?此外,所讨论的代码与我认为的例外情况不同,因为缺少字段org.x1c1b.sqlitecompass.config.provider.FXMLLoaderProvider.injector
。除此之外,对于使用Field.setAccessible
,它似乎还需要在guice端进行修复。值得向他汇报。(您可能会发现一个已经报告)请参阅GitHub,它似乎与您的问题非常相关。@Naman表示这是一个内部错误,对吗?我不能做太多…我的意思是,从问题来看,这是否可以在您这边解决还不是很清楚,例如,添加--添加打开…
的调整仍然有效。但在代码真正确定扩展之前,还有什么样的例外?guice是否需要访问您的其他包中的类?(如果它需要访问javafx类,除非您想向jvm/编译器添加选项,否则您似乎无法控制它。)此外,我建议限制对guice的访问;否则每个人都可以在你的课堂上使用反思。(打开org.x1c1b.sqlitecompass.config.provider到com.google.guice;
)