java - 当 module-info.java 存在时 findResource("") 返回 null,这是为什么呢?
问题描述
我正在调试为什么在module-info.java
我的 Spring Boot 应用程序中存在启动时spring-orm
抛出异常。这是一个例外:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NoClassDefFoundError: javax/transaction/UserTransaction
at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1699) ~[spring-beans-5.0.8.RELEASE.jar:na]
at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:573) ~[spring-beans-5.0.8.RELEASE.jar:na]
at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495) ~[spring-beans-5.0.8.RELEASE.jar:na]
at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317) ~[spring-beans-5.0.8.RELEASE.jar:na]
at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.0.8.RELEASE.jar:na]
at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-5.0.8.RELEASE.jar:na]
at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.8.RELEASE.jar:na]
at spring.context@5.0.8.RELEASE/org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1089) ~[spring-context-5.0.8.RELEASE.jar:na]
at spring.context@5.0.8.RELEASE/org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:859) ~[spring-context-5.0.8.RELEASE.jar:na]
at spring.context@5.0.8.RELEASE/org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.0.8.RELEASE.jar:na]
at spring.boot@2.0.4.RELEASE/org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.4.RELEASE.jar:na]
at spring.boot@2.0.4.RELEASE/org.springframework.boot.SpringApplication.refresh(SpringApplication.java:762) [spring-boot-2.0.4.RELEASE.jar:na]
at spring.boot@2.0.4.RELEASE/org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:398) [spring-boot-2.0.4.RELEASE.jar:na]
at spring.boot@2.0.4.RELEASE/org.springframework.boot.SpringApplication.run(SpringApplication.java:330) [spring-boot-2.0.4.RELEASE.jar:na]
at spring.boot@2.0.4.RELEASE/org.springframework.boot.SpringApplication.run(SpringApplication.java:1258) [spring-boot-2.0.4.RELEASE.jar:na]
at spring.boot@2.0.4.RELEASE/org.springframework.boot.SpringApplication.run(SpringApplication.java:1246) [spring-boot-2.0.4.RELEASE.jar:na]
at tech.flexpoint.dashmanserver/tech.flexpoint.dashmanserver.DashmanServerApplication.main(DashmanServerApplication.java:13) [classes/:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
at java.base/java.lang.reflect.Method.invoke(Method.java:564) ~[na:na]
at spring.boot.devtools@2.0.4.RELEASE/org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49) [spring-boot-devtools-2.0.4.RELEASE.jar:na]
Caused by: java.lang.NoClassDefFoundError: javax/transaction/UserTransaction
at java.base/java.lang.Class.getDeclaredMethods0(Native Method) ~[na:na]
at java.base/java.lang.Class.privateGetDeclaredMethods(Class.java:3119) ~[na:na]
at java.base/java.lang.Class.privateGetPublicMethods(Class.java:3144) ~[na:na]
at java.base/java.lang.Class.getMethods(Class.java:1863) ~[na:na]
at hibernate.core@5.2.17.Final/org.hibernate.service.internal.AbstractServiceRegistryImpl.applyInjections(AbstractServiceRegistryImpl.java:288) ~[hibernate-core-5.2.17.Final.jar:na]
at hibernate.core@5.2.17.Final/org.hibernate.service.internal.AbstractServiceRegistryImpl.injectDependencies(AbstractServiceRegistryImpl.java:279) ~[hibernate-core-5.2.17.Final.jar:na]
at hibernate.core@5.2.17.Final/org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:239) ~[hibernate-core-5.2.17.Final.jar:na]
at hibernate.core@5.2.17.Final/org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:210) ~[hibernate-core-5.2.17.Final.jar:na]
at hibernate.core@5.2.17.Final/org.hibernate.service.internal.SessionFactoryServiceRegistryImpl.getService(SessionFactoryServiceRegistryImpl.java:80) ~[hibernate-core-5.2.17.Final.jar:na]
at hibernate.core@5.2.17.Final/org.hibernate.internal.SessionFactoryImpl.canAccessTransactionManager(SessionFactoryImpl.java:942) ~[hibernate-core-5.2.17.Final.jar:na]
at hibernate.core@5.2.17.Final/org.hibernate.internal.SessionFactoryImpl.buildCurrentSessionContext(SessionFactoryImpl.java:953) ~[hibernate-core-5.2.17.Final.jar:na]
at hibernate.core@5.2.17.Final/org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:319) ~[hibernate-core-5.2.17.Final.jar:na]
at hibernate.core@5.2.17.Final/org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:462) ~[hibernate-core-5.2.17.Final.jar:na]
at hibernate.core@5.2.17.Final/org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:892) ~[hibernate-core-5.2.17.Final.jar:na]
at spring.orm@5.0.8.RELEASE/org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57) ~[spring-orm-5.0.8.RELEASE.jar:na]
at spring.orm@5.0.8.RELEASE/org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) ~[spring-orm-5.0.8.RELEASE.jar:na]
at spring.orm@5.0.8.RELEASE/org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390) ~[spring-orm-5.0.8.RELEASE.jar:na]
at spring.orm@5.0.8.RELEASE/org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:377) ~[spring-orm-5.0.8.RELEASE.jar:na]
at spring.orm@5.0.8.RELEASE/org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341) ~[spring-orm-5.0.8.RELEASE.jar:na]
at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1758) ~[spring-beans-5.0.8.RELEASE.jar:na]
at spring.beans@5.0.8.RELEASE/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1695) ~[spring-beans-5.0.8.RELEASE.jar:na]
... 21 common frames omitted
Caused by: java.lang.ClassNotFoundException: javax.transaction.UserTransaction
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:582) ~[na:na]
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:190) ~[na:na]
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:499) ~[na:na]
... 42 common frames omitted
我将问题追踪到URLClassLoader.findResource("")
返回null
是否module-info.java
存在但"file:/C:/Users/pupeno/Documents/Dashman/code/dashmanserver/target/classes/"
如果不存在。
我创建了引发相同异常的最小可能示例。要运行它,您需要:
- 从这里克隆并安装 Moditect 的最新副本:https ://github.com/moditect/moditect由于此错误修复尚未发布:https ://github.com/moditect/moditect/issues/51
- 从: https ://github.com/dashmantech/demo克隆演示 repo
- 使用凭证 demo/confi/application.properties 设置本地 PostgreSQL 数据库
- 先运行
mvn clean package
,让ModiTec创建所有模块 - 在 IntelliJ 的最新副本中打开项目
- 单击“运行演示”配置文件的播放(该
.idea
目录包含在适当的运行配置文件中,带有参数等)。
我需要findResource("")
返回"file:/C:/Users/pupeno/Documents/Dashman/code/dashmanserver/target/classes/"
,这样spring-orm
才能工作。
findResource("")
看起来像这样:
public URL findResource(final String name) {
/*
* The same restriction to finding classes applies to resources
*/
URL url = AccessController.doPrivileged(
new PrivilegedAction<>() {
public URL run() {
return ucp.findResource(name, true);
}
}, acc);
return url != null ? URLClassPath.checkURL(url) : null;
}
所以我可以看到在不使用模块系统的情况下可以进行一些访问,但是当module-infe.java
存在 a 时,Java 的模块系统会阻止它。我的问题是我不知道如何使它工作,应该导出或打开什么才能工作?
Spring Boot 导致该方法调用的方式是通过RestartClassLoader
的子类URLClassLoader
,特别是第 124 行,它调用super.findResource(name)
:
@Override
public URL findResource(String name) {
final ClassLoaderFile file = this.updatedFiles.getFile(name);
if (file == null) {
return super.findResource(name);
}
if (file.getKind() == Kind.DELETED) {
return null;
}
return AccessController
.doPrivileged((PrivilegedAction<URL>) () -> createFileUrl(name, file));
}
正在使用的特定RestartClassLoader
实例是其中的成员,ClassPathResource
并且它是这样定义的:
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
在构造函数中,第 85 行。
最后,getDefaultClassLoader()
看起来像这样:
/**
* Return the default ClassLoader to use: typically the thread context
* ClassLoader, if available; the ClassLoader that loaded the ClassUtils
* class will be used as fallback.
* <p>Call this method if you intend to use the thread context ClassLoader
* in a scenario where you clearly prefer a non-null ClassLoader reference:
* for example, for class path resource loading (but not necessarily for
* {@code Class.forName}, which accepts a {@code null} ClassLoader
* reference as well).
* @return the default ClassLoader (only {@code null} if even the system
* ClassLoader isn't accessible)
* @see Thread#getContextClassLoader()
* @see ClassLoader#getSystemClassLoader()
*/
@Nullable
public static ClassLoader getDefaultClassLoader() {
ClassLoader cl = null;
try {
cl = Thread.currentThread().getContextClassLoader();
}
catch (Throwable ex) {
// Cannot access thread context ClassLoader - falling back...
}
if (cl == null) {
// No thread context class loader -> use class loader of this class.
cl = ClassUtils.class.getClassLoader();
if (cl == null) {
// getClassLoader() returning null indicates the bootstrap ClassLoader
try {
cl = ClassLoader.getSystemClassLoader();
}
catch (Throwable ex) {
// Cannot access system ClassLoader - oh well, maybe the caller can live with null...
}
}
}
return cl;
}
我的module-info.java
包含:
module tech.flexpoint.dashman {
exports tech.flexpoint.dashman to com.fasterxml.jackson.databind;
exports tech.flexpoint.dashman.controllers.configurator to javafx.fxml;
opens tech.flexpoint.dashman to javafx.graphics, jna;
opens tech.flexpoint.dashman.controllers.common to javafx.fxml;
opens tech.flexpoint.dashman.controllers.configurator to javafx.fxml;
opens tech.flexpoint.dashman.models to org.hibernate.validator, tech.flexpoint.dashmancommon, javafx.base;
opens common;
opens configurator;
opens displayer;
opens winscreensaver;
requires appdirs;
requires org.bouncycastle.provider;
requires com.fasterxml.jackson.core;
requires com.fasterxml.jackson.databind;
requires com.fasterxml.jackson.datatype.jdk8;
requires io.sentry;
requires jackson.annotations;
requires java.desktop;
requires java.sql;
requires java.validation;
requires javafx.controls;
requires javafx.fxml;
requires javafx.graphics;
requires javafx.media;
requires javafx.web;
requires jna;
requires jna.platform;
requires org.apache.commons.lang3;
requires org.kordamp.ikonli.javafx;
requires org.kordamp.ikonli.fontawesome5;
requires spring.core;
requires spring.retry;
requires spring.web;
requires tech.flexpoint.dashmancommon;
}
在 IntelliJ 中,我启用了这些插件:
- 龙目岛插件
- .ginore
- 电源外壳
- VisualVM 启动器
- ANSI 荧光笔
- 批处理脚本支持
- 字节码查看器
- CMD 支持
- 版权
- 覆盖范围
- CSS 支持
- 数据库工具和 SQL
- Git 集成
- GitHub
- 摇篮
- 时髦的
- Heroku 集成
- HTML 工具
- HTTP 客户端
- 用于 Java 的 l18n
- IDE 设置同步
- Java 字节码反编译器
- Java EE:EJB、JPA、Servlet
- Java 流调试器
- JavaFX
- JUnit
- 行分拣机
- 降价支持
- Maven 集成
- Maven 集成扩展
- 持久性框架支持
- 属性支持
- 斯马里支持
- Spring AOP/@AspectJ
- 春季批次
- 弹簧靴
- 弹簧数据
- Spring 集成模式
- 春天的 OSGi
- 春季安全
- 弹簧支持
- 春季网络服务
- Spring WebSocket
- 终端
- YAML
解决方案
我注意到的一件事是您的应用程序(假设它被打包在 中tech.flexpoint.dashman
)似乎没有以任何方式向 Spring 开放,这肯定会导致类加载失败/非法访问。
我希望在module-info.java
(取决于您的 Spring 依赖项)中看到类似的内容:
opens tech.flexpoint.dashman to spring.core, spring.beans, spring.context;
异常是 a ,当无法解析在编译时已知NoClassDefFoundError
的类的类定义时,在运行时抛出异常,在这种情况下,接口是Java Transaction API (JTA)的一部分。javax.transaction.UserTransaction
正如其他人所指出的,JTA 没有与 JDK 捆绑在一起,需要作为编译依赖项添加。但是,需要加载UserTransaction
类定义的类来自spring-boot-autoconfigure
工件,它负责自己的依赖项(),因此您不需要添加 JTA 作为依赖项。spring-boot-autoconfigure@2.4.0.RELEASE
jboss-transaction-spi@7.6.0.Final
jboss-transaction-api_1.2_spec@1.1.1.Final
但是,由于您想将自己的应用程序打包为 Java 9 模块,因此需要显式声明其依赖项。spring-boot-autoconfigure
还不是模块化的 Java 9 库,并且不会为您执行此操作(即传递性)。JTA 的自动模块名称为java.transaction
,因此您需要在以下位置添加需求module-info.java
:
requires java.transaction;
NoClassDefFoundError
我让您的示例运行,并且在从 IntelliJ IDEA 运行时确实得到了s 。堆栈跟踪指向 a ClassNotFoundException
,表示类路径问题。由于 IDEA 在从那里启动应用程序时会计算类路径,所以我想看看在使用spring-boot-maven-plugin
运行应用程序时是否可以重现错误。
我将IDEA运行配置复制到spring-boot-maven-plugin
配置中,如下图:
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<mainClass>tech.flexpoint.demo.DemoApplication</mainClass>
<jvmArguments>--show-module-resolution --add-opens=java.base/java.lang=spring.core --add-opens=java.base/java.io=tomcat.embed.core --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED</jvmArguments>
<workingDirectory>${project.basedir}</workingDirectory>
</configuration>
</plugin>
然后我调用mvn spring-boot:run
并瞧,应用程序成功启动而没有错误。我只能得出结论,这是 IntelliJ 计算的类路径的问题。
推荐阅读
- json - XML 和 JSON 格式的 Django REST API
- xamarin.android - 将目标 android 版本从 7.1 更改为 8.1 显示错误(xamarin)
- ios - 如何在 Swift 的数组中获取所有可用的表情符号?
- mongodb - 在 MongoEngine 中使用 __raw__ 访问 ListField/EmbeddedDocumentListField 中的项目
- javascript - 如何使用 JavaScript 检测冷门浏览器(百度)?
- python - 从烧瓶中,如何将 matplotlib 绘图图像发送到 JSON 对象?
- c# - WSDL 生成的类型不采用 COM 属性
- c - 使用结构的数组排序
- reactjs - 创建 redux reducer 以更改状态嵌套对象值
- ansible - Ansible Debug msg 重定向作为文件