java spring-boot java-10 java-module jigsaw

findResource("") devuelve null cuando module-info.java está presente, ¿por qué?



java 9 modular (5)

Como mencionó en su problema original, el código funciona sin module-info.java pero no con module-info.java. Veo que ha hecho todo este trabajo duro para explicar el problema, crear un proyecto mínimo y demás para profundizar en el problema.

En cuanto a su problema, es obvio que uno de los módulos está causando que el URLClassLoader.findResource("") devuelva null . Podría ser uno de los módulos de la lista que está anulando este método de clase o tiene una implementación ambigua.

¿Por qué no comienzas con un módulo vacío -info.java para el ejemplo mínimo y sigues agregando 1 módulo a la vez hasta que veamos el error? Creo que esto nos ayudará a encontrar al culpable.

Estoy depurando por qué en presencia de module-info.java en mi aplicación Spring Boot, spring-orm lanza una excepción durante el tiempo de arranque. Esta es la excepción:

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("") el problema en URLClassLoader.findResource("") devolviendo null si module-info.java está presente pero "file:/C:/Users/pupeno/Documents/Dashman/code/dashmanserver/target/classes/" if no es.

mvn clean package ejemplo mínimo posible que arroja la misma excepción: https://github.com/dashmantech/demo Para ejecutarlo, primero debe ejecutar mvn clean package , para que ModiTec cree todos los módulos y luego pueda ejecutar el .idea Spring de IntelliJ (el directorio .idea se incluye con el perfil de ejecución apropiado, con argumentos, etc.).

Necesito findResource("") para devolver "file:/C:/Users/pupeno/Documents/Dashman/code/dashmanserver/target/classes/" para que spring-orm pueda funcionar.

findResource("") ve así:

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; }

Así que puedo ver que hay algún acceso que está funcionando bien sin usar el sistema de módulos, pero el sistema de módulos de Java lo previene cuando hay un module-infe.java presente. Mi problema es que no veo cómo hacer que funcione, ¿qué debería exportar o abrir para que funcione?

La forma en que Spring Boot está causando la llamada de ese método es a través de RestartClassLoader , una subclase de URLClassLoader , específicamente, la línea 124 que llama a super.findResource(name) en:

@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)); }

La instancia específica de RestartClassLoader se utiliza es miembro de ClassPathResource y está definida de esta manera:

this.classLoader = (classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader());

en el constructor, línea 85 .

Por último, getDefaultClassLoader() ve así:

/** * 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; }

Mi module-info.java contiene:

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; }


No veo ninguna dependencia relacionada con javax.transaction en su configuración module-info.java . ¿Puede estar escondido en algún lugar de tu submódulo?

Una cosa más que vale la pena comprobar es que java.transaction no se resuelve por defecto

Es posible que desee incluir el módulo javax.transaction lugar:

requires javax.transaction.api;


Sin la excepción de la pila, no hay mucho que hacer aquí.

Una cosa que noto es que su aplicación (suponiendo que esté empaquetada en tech.flexpoint.dashman ) no parece estar abierta para Spring de ninguna manera, lo que seguramente resultará en una carga de clase / acceso ilegal fallida.

Esperaría ver algo como esto en module-info.java (dependiendo de las dependencias de Spring):

opens tech.flexpoint.dashman to spring.core, spring.beans, spring.context;

No sé si esto es relevante, como se mencionó anteriormente, debe incluir el seguimiento de la pila.

EDIT 1

Gracias por el seguimiento de la pila.

La excepción es un NoClassDefFoundError , que se lanza en tiempo de ejecución cuando no se puede resolver la definición de clase de una clase conocida en tiempo de compilación , en este caso la interfaz javax.transaction.UserTransaction , que es parte de Java Transaction API (JTA) .

Como han señalado otros, JTA no está incluido con el JDK, y debe agregarse como una dependencia de compilación. Sin embargo, la clase que necesita cargar la definición de la clase UserTransaction proviene del artefacto de spring-boot-autoconfigure , que es responsable de sus propias dependencias ( [email protected] -> [email protected] -> [email protected] ), por lo que no debería necesitar agregar JTA como una dependencia.

Sin embargo, debido a que desea empaquetar su propia aplicación como un módulo de Java 9, necesita indicar explícitamente sus dependencias . spring-boot-autoconfigure aún no es una biblioteca modularizada de Java 9, y no hace esto por usted (es decir, transitivamente). El nombre del módulo automático para JTA es java.transaction , por lo que debe agregar el requisito en module-info.java :

requires java.transaction;

EDIT 2 - Solución

Después de tocar un poco (compilar una versión instantánea de moditect, crear la demo base de datos postgresql, agregar META-INF/orm.xml como raíz de la unidad de persistencia), obtuve su ejemplo en ejecución y de hecho NoClassDefFoundException al ejecutar desde IntelliJ IDEA. La stacktrace apuntaba a una ClassNotFoundException , que indica problemas de classpath. Como IDEA calcula el classpath al iniciar la aplicación desde allí, quería ver si podía reproducir el error al usar el spring-boot-maven-plugin para ejecutar la aplicación.

Copié la configuración de ejecución de IDEA a la configuración de spring-boot-maven-plugin , como se muestra a continuación:

<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>

Luego mvn spring-boot:run y voila, la aplicación se inició correctamente sin errores.

Solo puedo concluir que este es un problema con el classpath calculado por IntelliJ. Espero que esto te ayude a dar un paso más.


Suponiendo que haya declarado la dependencia:

<dependency> <groupId>javax.transaction</groupId> <artifactId>javax.transaction-api</artifactId> <version>1.3</version> </dependency>

Incluye lo siguiente en module-info.java :

requires java.transaction;

La versión 1.3 declara el nombre del módulo automático, mientras que la versión 1.2 no.
El último requires javax.transaction.api; . Source


este problema (o similar) ya se había archivado para Spring-boot en GitHub (pero con Java 9).

Tendría moditect bajo sospecha, aunque también hay problemas para moditect en GitHub y también he encontrado su issue allí; la actualización de ASM a 6.2.1 corrige al menos otro cambio de interrupción:

<dependency> <groupId>org.ow2.asm</groupId> <artifactId>asm</artifactId> <version>6.2.1</version> </dependency>