我正在调试为什么在Spring Boot应用程序中存在module-info.java时,spring-orm在启动期间会引发异常。这是异常(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 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
  • 使用凭证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时,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());
    

    在构造函数line 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中,我启用了以下插件:
  • Lombok插件
  • .ginore
  • PowerShell
  • VisualVM启动器
  • ANSI荧光笔
  • 批处理脚本支持
  • 字节码查看器
  • CMD支持
  • 版权所有
  • 覆盖范围
  • CSS支持
  • 数据库工具和SQL
  • Git集成
  • GitHub
  • Gradle
  • Groovy
  • Heroku集成
  • HTML工具
  • HTTP客户端
  • Java的
  • l18n
  • IDE设置同步
  • Java字节码反编译器
  • Java EE:EJB,JPA,Servlet
  • Java流调试器
  • JavaFX
  • JUnit
  • 行分类器
  • Markdown支持
  • Maven集成
  • Maven集成扩展
  • 持久性框架支持
  • 属性支持
  • Smali支持
  • Spring AOP/@ AspectJ
  • Spring 批
  • Spring 启动
  • Spring 数据
  • Spring 集成模式
  • Spring OSGi
  • Spring Security
  • Spring支持
  • Spring Web服务
  • Spring WebSocket
  • 终端
  • YAML
  • 最佳答案

    我注意到的一件事是,您的应用程序(假设它打包在tech.flexpoint.dashman中)似乎没有以任何方式向Spring开放,这肯定会导致类加载/非法访问失败。

    我希望在module-info.java中看到类似这样的内容(取决于您的Spring依赖项):

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

    异常(exception)是NoClassDefFoundError,当无法解析在编译时已知的类的类定义时会在运行时抛出,在这种情况下,该接口(interface)是Java Transaction API (JTA)的一部分javax.transaction.UserTransaction

    正如其他人指出的那样,JTA没有与JDK bundle 在一起,需要作为编译依赖项添加。但是,需要加载UserTransaction类定义的类来自spring-boot-autoconfigure工件,该工件负责其自己的依赖项( spring-boot-autoconfigure@2.4.0.RELEASE 🡒 jboss-transaction-spi@7.6.0.Final 🡒 jboss-transaction-api_1.2_spec@1.1.1.Final ),因此您无需添加JTA作为依赖项。

    但是,由于您要将自己的应用程序打包为Java 9模块,因此需要explicitly state its dependenciesspring-boot-autoconfigure尚不是模块化的Java 9库,因此不会为您(即,传递地)执行此操作。 JTA的自动模块名称为java.transaction,因此您需要在module-info.java中添加需求:
    requires java.transaction;
    

    我运行了您的示例,并且从IntelliJ IDEA运行时确实获得了NoClassDefFoundError。 stacktrace指向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,该应用程序成功启动,没有错误。
    我只能得出结论,这是IntelliJ计算出的类路径的问题。

    10-07 19:09
    查看更多