是什么导致 Spring 在使用 AnnotationConfigApplicationContext 时无法加载配置 class?

What is causing Spring not to be able to load the configuration class when using AnnotationConfigApplicationContext?

我最近 运行 遇到过这个问题,在搜索 Whosebug 网站后,找不到可行的解决方案。我收到的错误是 Spring 在加载配置 class 时遇到问题。我正在使用注释和 Spring class 来配置,而不是 XML 文件,以练习使用注释而不是 XML 文件配置 Java 应用程序.

我已经仔细检查以确保需要的注释在那里,并且 Spring 配置文件被标记为 @Configuration 并且配置文件中的所有 bean 都被标记为 @Bean.此外,特定的 bean 标有 @Component.

创建测试 class 后,似乎 Spring 仍然无法找到配置文件。我仔细检查了 pom 文件,看看是否缺少任何依赖项,但据我所知,似乎需要的依赖项就在那里。我不确定我还遗漏了什么,或者为什么 Spring 找不到配置文件。任何帮助将不胜感激。

这是我的 pom.xml 文件:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>com.spring</groupId>
  <artifactId>Spring_JavaConfig</artifactId>
  <version>0.0.1-SNAPSHOT</version>

  <name>Spring_JavaConfig</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <springframework.version>4.3.6.RELEASE</springframework.version>
  </properties>

  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>${springframework.version}</version>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>${springframework.version}</version>
    </dependency>
  </dependencies>
  <build>
    <pluginManagement>
      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.2</version>
          <configuration>
            <source>1.8</source>
            <target>1.8</target>
          </configuration>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

这是我的 Java 文件:

Dao class(不是真正访问数据库,只是练习使用 Spring 注释而不是 XML 文件进行配置):

package com.spring;

import org.springframework.stereotype.Component;

@Component
public class Dao {
  public void create() {
    System.out.println("Created");
  }
}

Spring 配置 class:

package com.spring;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class SpringConfig {
  @Bean
  public Dao dao() {
    return new Dao();
  }
}

测试class:

package com.spring;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Test {
  public static void main(String[] args) {
    AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    Dao dao = context.getBean(Dao.class);
    dao.create();
    context.close();
  }
}

这是我遇到的错误:

Feb 28, 2022 1:39:12 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@39c0f4a: startup date [Mon Feb 28 13:39:12 EST 2022]; root of context hierarchy
Exception in thread "main" java.lang.IllegalStateException: Cannot load configuration class: com.spring.SpringConfig
at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:403)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanFactory(ConfigurationClassPostProcessor.java:249)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:281)
at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:125)
at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:686)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:524)
at org.springframework.context.annotation.AnnotationConfigApplicationContext.<init>(AnnotationConfigApplicationContext.java:84)
at com.spring.Test.main(Test.java:9)
Caused by: java.lang.ExceptionInInitializerError
at org.springframework.context.annotation.ConfigurationClassEnhancer.newEnhancer(ConfigurationClassEnhancer.java:119)
at org.springframework.context.annotation.ConfigurationClassEnhancer.enhance(ConfigurationClassEnhancer.java:107)
at org.springframework.context.annotation.ConfigurationClassPostProcessor.enhanceConfigurationClasses(ConfigurationClassPostProcessor.java:393)
... 7 more
Caused by: java.lang.IllegalStateException: Unable to load cache item
at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:79)
at org.springframework.cglib.core.internal.LoadingCache.get(LoadingCache.java:34)
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.get(AbstractClassGenerator.java:116)
at org.springframework.cglib.core.AbstractClassGenerator.create(AbstractClassGenerator.java:291)
at org.springframework.cglib.core.KeyFactory$Generator.create(KeyFactory.java:221)
at org.springframework.cglib.core.KeyFactory.create(KeyFactory.java:174)
at org.springframework.cglib.core.KeyFactory.create(KeyFactory.java:153)
at org.springframework.cglib.proxy.Enhancer.<clinit>(Enhancer.java:73)
... 10 more
Caused by: java.lang.ExceptionInInitializerError
at org.springframework.cglib.core.KeyFactory$Generator.generateClass(KeyFactory.java:243)
at org.springframework.cglib.core.DefaultGeneratorStrategy.generate(DefaultGeneratorStrategy.java:25)
at org.springframework.cglib.core.AbstractClassGenerator.generate(AbstractClassGenerator.java:329)
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.apply(AbstractClassGenerator.java:93)
at org.springframework.cglib.core.AbstractClassGenerator$ClassLoaderData.apply(AbstractClassGenerator.java:91)
at org.springframework.cglib.core.internal.LoadingCache.call(LoadingCache.java:54)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at org.springframework.cglib.core.internal.LoadingCache.createEntry(LoadingCache.java:61)
... 17 more
Caused by: java.lang.reflect.InaccessibleObjectException: Unable to make protected final java.lang.Class java.lang.ClassLoader.defineClass(java.lang.String,byte[],int,int,java.security.ProtectionDomain) throws java.lang.ClassFormatError accessible: module java.base does not "opens java.lang" to unnamed module @2f8f5f62
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:354)
at java.base/java.lang.reflect.AccessibleObject.checkCanSetAccessible(AccessibleObject.java:297)
at java.base/java.lang.reflect.Method.checkCanSetAccessible(Method.java:199)
at java.base/java.lang.reflect.Method.setAccessible(Method.java:193)
at org.springframework.cglib.core.ReflectUtils.run(ReflectUtils.java:54)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:318)
at org.springframework.cglib.core.ReflectUtils.<clinit>(ReflectUtils.java:44)
... 25 more

自 Java 9、Java introduced Modules 和一些框架,如 Spring,依赖于多个反射行为,并且由于模块化,一些 类 没有它们的属性通过反射访问。 解决方案是更改这些属性的可访问性,但这需要很长时间。

真正的解决方案是在您 运行 您的应用程序时添加此 VM 选项:--add-opens java.base/java.lang=ALL-UNNAMED

请参考另一个有类似问题的 post 上的这个很好的答案:

我把你的应用改成了SpringBoot,你考虑用吗? 对于 Spring 应用程序,这是一个非常方便的解决方案,因为它抽象了许多配置。这是一个带有详细示例的 link:Spring Boot Example

前言

这个问题实际上与我一般回答的问题非常相似 here,其中我还描述了 --add-opens 解决方法@victoraugustofd在他的回答中。

但是因为另一个答案不包含 MCVE 而这个答案包含,所以这个问题实际上最好不仅以通用的方式而且以更具体的方式回答,因为每个 reader将来可以查看代码和 POM,以便重现问题和解决方案。这就是我们想要在 Stack Overflow 上提出的问题。

问题根本原因

就像我在另一个答案中所说的那样,问题是 JEP 396 限制了对内部 JDK 类 的访问,因为 JDK 16.

解决方案

因此,如果您想避免打开包的解决方法,只需升级到嵌入更新的 CGLIB 版本的更新的 Spring 版本。对于 4.3.x,没有这样的更新,对于 5.0.x 也没有。但是如果你例如可以升级到 5.1.20、5.2.19 或 5.3.16,您的示例应用程序适用于 JDK 16+。可能有稍旧的 minor-minor 版本包含合适的 CGLIB 版本,但如果您仍然升级,为什么还要费心呢?只需为 5.1、5.2 或 5.3 使用最新的 minor-minor。