Je débogage pourquoi, en présence de module-info.Java
dans mon application Spring Boot, spring-orm
lève une exception au moment du démarrage. C'est l'exception:
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
J'ai suivi le problème jusqu'à URLClassLoader.findResource("")
renvoyant null
si module-info.Java
est présent, mais "file:/C:/Users/pupeno/Documents/Dashman/code/dashmanserver/target/classes/"
si ce n'est pas le cas.
J'ai créé l'exemple minimum possible qui lève la même exception. Pour l'exécuter, vous devez:
mvn clean package
pour que ModiTec crée tous les modules..idea
est inclus avec le profil d’exécution approprié, avec les arguments, etc.).J'ai besoin de findResource("")
pour renvoyer "file:/C:/Users/pupeno/Documents/Dashman/code/dashmanserver/target/classes/"
afin que spring-orm
puisse fonctionner.
findResource("")
ressemble à ceci:
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;
}
Je peux donc constater que certains accès sont autorisés sans utiliser le système de modules, mais le système de modules de Java l'empêche en présence d'un module-infe.Java
. Mon problème est que je ne vois pas comment le faire fonctionner, ce qui devrait être exporté ou ouvert pour que cela fonctionne.
La manière dont Spring Boot provoque l'appel de cette méthode passe par RestartClassLoader
, une sous-classe de URLClassLoader
, en particulier la ligne 124 qui appelle super.findResource(name)
dans:
@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));
}
L'instance spécifique RestartClassLoader
utilisée est un membre de ClassPathResource
et est définie comme suit:
this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());
dans le constructeur, ligne 85 .
Enfin, getDefaultClassLoader()
ressemble à ceci:
/**
* 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;
}
Mon module-info.Java
contient:
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;
}
Dans IntelliJ, ces plugins sont activés:
En supposant que vous ayez déclaré la dépendance:
<dependency>
<groupId>javax.transaction</groupId>
<artifactId>javax.transaction-api</artifactId>
<version>1.3</version>
</dependency>
Inclure les éléments suivants dans module-info.Java
:
requires Java.transaction;
La version 1.3 déclare le nom du module automatique, contrairement à la version 1.2.
Ce dernier requires javax.transaction.api;
. La source
Comme vous l'avez mentionné dans votre problème initial, le code fonctionne sans module-info.Java mais pas avec le module-info.Java. Je peux voir que vous avez fait tout ce travail difficile pour expliquer le problème, créer un projet minimal, etc. pour aller au fond des choses.
En regardant votre problème, il est évident que l’un des modules cause la fonction URLClassLoader.findResource("")
renvoyant null
. Il se peut que l’un des modules en bas de la liste remplace cette méthode de classe ou ait une implémentation ambiguë.
Pourquoi ne commencez-vous pas avec un module-info.Java vide pour l'exemple minimal et continuez-vous à ajouter un module à la fois jusqu'à ce que nous voyions l'erreur? Je crois que cela nous aidera à trouver le coupable.
ce problème (ou un problème similaire) avait déjà été déposé pour spring-boot sur GitHub (mais avec Java 9).
J'aurais modifié sous le soupçon, alors qu'il y a aussi des questions déposées pour moditect
sur GitHub et j'ai également trouvé votre problème là; mettre à jour ASM avec 6.2.1
corrige au moins un autre changement radical:
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
<version>6.2.1</version>
</dependency>