web-dev-qa-db-fra.com

findResource ("") renvoie null lorsque module-info.Java est présent, pourquoi?

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:

  1. Clonez et installez une copie récente de Moditect à partir d'ici: https://github.com/moditect/moditect car ce correctif logiciel n'est pas encore disponible: https://github.com/moditect/moditect/ numéros/51
  2. Clonez le dépôt de démonstration à partir de: https://github.com/dashmantech/demo
  3. Configurer une base de données PostgreSQL locale avec les informations d'identification demo/confi/application.properties
  4. Exécutez d'abord mvn clean package pour que ModiTec crée tous les modules.
  5. Ouvrez le projet dans une copie récente d'IntelliJ
  6. Cliquez sur Play pour le profil "Run Demo" (le répertoire .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:

  • Lombok Plugin
  • .ginore
  • PowerShell
  • Lanceur VisualVM
  • Surligneur ANSI
  • Prise en charge des scripts par lots
  • Visionneuse de Bytecode
  • CMD Support
  • Droits d'auteur
  • Couverture
  • Support CSS
  • Outils de base de données et SQL
  • Intégration Git
  • GitHub
  • Gradle
  • Sensationnel
  • Intégration de Heroku
  • Outils HTML
  • Client HTTP
  • l18n pour Java
  • Synchronisation des paramètres IDE
  • Decompiler Java Bytecode
  • Java EE: EJB, JPA, Servlets
  • Débogueur Java Stream
  • JavaFX
  • JUnit
  • Trieur de lignes
  • Soutien Markdown
  • Intégration Maven
  • Extension d'intégration Maven
  • Prise en charge des cadres de persistance
  • Support des propriétés
  • Smali Support
  • Printemps AOP/@ AspectJ
  • Lot de printemps
  • Botte de printemps
  • Données de printemps
  • Printemps Intégration Patterns
  • Printemps OSGi
  • Sécurité de printemps
  • Support de printemps
  • Services Web Spring
  • Spring WebSocket
  • Terminal
  • YAML
48
pupeno

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

3
Francesco Menzani

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.

1
Rinsad Ahmed

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>
0
Martin Zeitler