findResource (""), возвращающий значение null, когда присутствует module-info.java, почему?
Я отлаживаю, почему при наличии module-info.java
в приложении Spring Boot Spring 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 [email protected]/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1699) ~[spring-beans-5.0.8.RELEASE.jar:na]
at [email protected]/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:573) ~[spring-beans-5.0.8.RELEASE.jar:na]
at [email protected]/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:495) ~[spring-beans-5.0.8.RELEASE.jar:na]
at [email protected]/org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:317) ~[spring-beans-5.0.8.RELEASE.jar:na]
at [email protected]/org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222) ~[spring-beans-5.0.8.RELEASE.jar:na]
at [email protected]/org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:315) ~[spring-beans-5.0.8.RELEASE.jar:na]
at [email protected]/org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199) ~[spring-beans-5.0.8.RELEASE.jar:na]
at [email protected]/org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1089) ~[spring-context-5.0.8.RELEASE.jar:na]
at [email protected]/org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:859) ~[spring-context-5.0.8.RELEASE.jar:na]
at [email protected]/org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550) ~[spring-context-5.0.8.RELEASE.jar:na]
at [email protected]/org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:140) ~[spring-boot-2.0.4.RELEASE.jar:na]
at [email protected]/org.springframework.boot.SpringApplication.refresh(SpringApplication.java:762) [spring-boot-2.0.4.RELEASE.jar:na]
at [email protected]/org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:398) [spring-boot-2.0.4.RELEASE.jar:na]
at [email protected]/org.springframework.boot.SpringApplication.run(SpringApplication.java:330) [spring-boot-2.0.4.RELEASE.jar:na]
at [email protected]/org.springframework.boot.SpringApplication.run(SpringApplication.java:1258) [spring-boot-2.0.4.RELEASE.jar:na]
at [email protected]/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 [email protected]/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 [email protected]/org.hibernate.service.internal.AbstractServiceRegistryImpl.applyInjections(AbstractServiceRegistryImpl.java:288) ~[hibernate-core-5.2.17.Final.jar:na]
at [email protected]/org.hibernate.service.internal.AbstractServiceRegistryImpl.injectDependencies(AbstractServiceRegistryImpl.java:279) ~[hibernate-core-5.2.17.Final.jar:na]
at [email protected]/org.hibernate.service.internal.AbstractServiceRegistryImpl.initializeService(AbstractServiceRegistryImpl.java:239) ~[hibernate-core-5.2.17.Final.jar:na]
at [email protected]/org.hibernate.service.internal.AbstractServiceRegistryImpl.getService(AbstractServiceRegistryImpl.java:210) ~[hibernate-core-5.2.17.Final.jar:na]
at [email protected]/org.hibernate.service.internal.SessionFactoryServiceRegistryImpl.getService(SessionFactoryServiceRegistryImpl.java:80) ~[hibernate-core-5.2.17.Final.jar:na]
at [email protected]/org.hibernate.internal.SessionFactoryImpl.canAccessTransactionManager(SessionFactoryImpl.java:942) ~[hibernate-core-5.2.17.Final.jar:na]
at [email protected]/org.hibernate.internal.SessionFactoryImpl.buildCurrentSessionContext(SessionFactoryImpl.java:953) ~[hibernate-core-5.2.17.Final.jar:na]
at [email protected]/org.hibernate.internal.SessionFactoryImpl.<init>(SessionFactoryImpl.java:319) ~[hibernate-core-5.2.17.Final.jar:na]
at [email protected]/org.hibernate.boot.internal.SessionFactoryBuilderImpl.build(SessionFactoryBuilderImpl.java:462) ~[hibernate-core-5.2.17.Final.jar:na]
at [email protected]/org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:892) ~[hibernate-core-5.2.17.Final.jar:na]
at [email protected]/org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:57) ~[spring-orm-5.0.8.RELEASE.jar:na]
at [email protected]/org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:365) ~[spring-orm-5.0.8.RELEASE.jar:na]
at [email protected]/org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:390) ~[spring-orm-5.0.8.RELEASE.jar:na]
at [email protected]/org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:377) ~[spring-orm-5.0.8.RELEASE.jar:na]
at [email protected]/org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.afterPropertiesSet(LocalContainerEntityManagerFactoryBean.java:341) ~[spring-orm-5.0.8.RELEASE.jar:na]
at [email protected]/org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1758) ~[spring-beans-5.0.8.RELEASE.jar:na]
at [email protected]/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/"
if не это.
Я создал минимально возможный пример, который вызывает одно и то же исключение. Чтобы запустить его, вам необходимо:
- Клонировать и установить последнюю копию Moditect отсюда: https://github.com/moditect/moditect из-за того, что исправление ошибки еще не завершено: https://github.com/moditect/moditect/issues/51
- Клонирование демо-репо: https://github.com/dashmantech/demo
- Настройка локальной базы данных PostgreSQL с учетными данными demo/confi/application.properties
- Сначала запустите
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;
}
Таким образом, я вижу, что доступ к этому окну осуществляется без использования модульной системы, но это предотвращается системой модулей Java, когда присутствует модуль module-infe.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
- PowerShell
- VisualVM Launcher
- ANSI Highlighter
- Поддержка пакетных скриптов
- Просмотрщик Bytecode
- Поддержка CMD
- авторское право
- покрытие
- Поддержка CSS
- Инструменты базы данных и SQL
- Интеграция Git
- GitHub
- Gradle
- Groovy
- Интеграция Heroku
- Инструменты HTML
- HTTP-клиент
- l18n для Java
- Синхронизация настроек IDE
- Декомпилятор байт-кода Java
- Java EE: EJB, JPA, сервлеты
- Отладчик Java Stream
- JavaFX
- JUnit
- Сортировщик строк
- Поддержка Markdown
- Интеграция Maven
- Расширение интеграции Maven
- Поддержка стойкости
- Свойства Поддержка
- Поддержка Smali
- Spring AOP/@AspectJ
- Весенняя партия
- Весенняя загрузка
- Весенние данные
- Весенние шаблоны интеграции
- Весна OSGi
- Весенняя безопасность
- Весенняя поддержка
- Весенние веб-службы
- Весенний WebSocket
- Терминал
- YAML
Ответы
Ответ 1
Одна вещь, которую я замечаю, заключается в том, что ваше приложение (при условии, что оно упаковано в tech.flexpoint.dashman
), похоже, не открывается до Spring каким-либо образом, что, несомненно, приведет к неудачной загрузке/незаконному доступу классов.
Я ожидал увидеть что-то подобное в module-info.java
(в зависимости от зависимостей от Spring):
opens tech.flexpoint.dashman to spring.core, spring.beans, spring.context;
Исключением является NoClassDefFoundError
, который NoClassDefFoundError
во время выполнения, когда определение класса класса, которое было известно во время компиляции, не может быть разрешено, в этом случае интерфейс javax.transaction.UserTransaction
, который является частью API транзакций Java (JTA),
Как указывали другие, JTA не поставляется вместе с JDK и нуждается в добавлении в качестве зависимости от компиляции. Однако класс, который должен загружать определение класса UserTransaction
исходит из артефакта spring-boot-autoconfigure
, который отвечает за свои собственные зависимости ( [email protected]
🡒 [email protected]
transaction- [email protected]
@ [email protected]
🡒 [email protected]
), поэтому вам не нужно добавлять JTA в качестве зависимости.
Однако, поскольку вы хотите упаковать свое приложение в качестве модуля Java 9, ему необходимо явно указать свои зависимости. spring-boot-autoconfigure
еще не является модульной библиотекой Java 9 и не делает этого для вас (т.е. транзитивно). Автоматическое имя модуля для JTA - это java.transaction
, поэтому вам нужно добавить требование в module-info.java
:
requires java.transaction;
Я получил ваш пример и действительно получил NoClassDefFoundError
при запуске из IntelliJ IDEA. Стекловатка указала на исключение 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
и voila, приложение успешно загрузилось без ошибок. Я могу только сделать вывод, что это проблема с classpath, рассчитанная IntelliJ.
Ответ 2
Предполагая, что вы заявили о зависимости:
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
<version>1.3</version>
</dependency>
Включите в module-info.java
следующее:
requires java.transaction;
Версия 1.3 объявляет имя автоматического модуля, а версия 1.2 - нет.
Последнее requires javax.transaction.api;
, Источник
Ответ 3
Как вы уже упоминали в своей исходной проблеме, код работает без module-info.java, но не с module-info.java. Я вижу, что вы проделали всю эту тяжелую работу, объясняя проблему, создавая минимальный проект и так далее, чтобы перейти к этой проблеме.
Рассмотрев вашу проблему, очевидно, что один из модулей вызывает URLClassLoader.findResource("")
возвращающий значение null
. Возможно, один из модулей в списке переопределяет этот метод класса или имеет неоднозначную реализацию.
Почему вы не начинаете с пустого module-info.java для минимального примера и продолжаете добавлять 1 модуль за один раз, пока не увидите ошибку? Я считаю, что это поможет нам найти виновника.
Ответ 4
эта (или аналогичная) проблема уже была подана на весеннюю загрузку на GitHub (но с Java 9).
У меня был бы подозрительный тип, хотя есть проблемы, связанные с moditect
GitHub, и я также нашел вашу проблему там; обновление ASM до 6.2.1
исправляет хотя бы одно другое изменение:
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>6.2.1</version>
</dependency>