NoSuchBeanDefinitionException / UnsatisfiedDependencyException 测试时 Spring 使用 Java 模块启动应用程序
NoSuchBeanDefinitionException / UnsatisfiedDependencyException when testing Spring Boot app with Java modules
在测试配置了 Java 9 个模块的多模式 Spring 引导应用程序时,如何减轻 NoSuchBeanDefinitionException
和相关 UnsatisfiedDependencyException
的发生?
将 module-info.java
个文件添加到我的多模块项目后,我的 Spring 引导应用程序测试开始失败:
测试失败
contextLoads Time elapsed: 0 s <<< ERROR!
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'second':
Unsatisfied dependency expressed through constructor parameter 0;
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'com.example.first.First' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'com.example.first.First' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
文件夹结构
parent
|
+ pom.xml
|
+-- first
| + pom.xml
| + src/main/java/module-info.java
| + src/main/java/com.example.first/First.java
|
+-- second
| + pom.xml
| + src/main/java/module-info.java
| + src/main/java/com.example.second/ApplicationConfig.java
| + src/main/java/com.example.second/Second.java
| + src/test/java/com.example.second/SecondTest.java
父模块
仅包含父 pom 文件的 Maven 父模块。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>parent</artifactId>
<version>0.1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<java.version>11</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>first</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>second</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>first</module>
<module>second</module>
</modules>
</project>
第一个模块
一个简单的 Java 模块,它只包含一个 Java class 并且确实有任何 Spring 依赖项。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>parent</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>first</artifactId>
</project>
First.java
package com.example.first;
public class First {
}
第一个模块-info.java
module com.example.first {
exports com.example.first;
opens com.example.first;
}
第二个模块
除了所需的 Spring 依赖项之外,还依赖于第一个模块的应用程序模块。它负责配置 Spring bean、运行 应用程序等
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>parent</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>second</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>first</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Second.java
package com.example.second;
import com.example.first.First;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Second {
private final First first;
public Second(First first) {
this.first = first;
}
public static void main(String[] args) {
SpringApplication.run(ApplicationConfig.class, args);
System.out.println("started");
}
}
ApplicationConfig.java
package com.example.second;
import com.example.first.First;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ApplicationConfig {
@Bean
First first() {
return new First();
}
}
SecondTest.java
package com.example.second;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SecondTest {
@Test
void contextLoads() {
}
}
观察
- 无论我是否使用 Maven,测试都会失败,例如
mvn test
或者如果我尝试从我的 IDE. 中调试它们
- 可以从 IDE 以及使用
java -jar second/target/second-0.1.0-SNAPSHOT.jar
的命令行启动调用 Second#main()
的应用程序,因此依赖注入和自动连接在此工作案例(在这种情况下,jar 文件必须在没有测试的情况下构建,因为它们失败了,例如 mvn package -DskipTests=true
)所以 First
bean 在这种情况下被正确创建和自动装配。
- 如果
module-info.java
个文件都被删除,则测试通过,所以错误似乎与 Java 个模块有关
根据@xerx593 评论中的反馈,我实施了一个解决方法,我将 @SpringBootApplication
class 提取到单独的 Application
class 并相应地更新了测试:
|
+-- second
| + pom.xml
| + src/main/java/module-info.java
| + src/main/java/com.example.second/Application.java
| + src/main/java/com.example.second/ApplicationConfig.java
| + src/main/java/com.example.second/Second.java
| + src/test/java/com.example.second/ApplicationTest.java
Application.java
package com.example.second;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
ApplicationConfig.java
package com.example.second;
import com.example.first.First;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ApplicationConfig {
@Bean
First first() {
return new First();
}
}
Second.java
package com.example.second;
import com.example.first.First;
import org.springframework.stereotype.Service;
@Service
public class Second {
private final First first;
public Second(First first) {
this.first = first;
}
}
ApplicationTest.java
package com.example.second;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(classes = Application.class)
class ApplicationTest {
@Test
void contextLoads() {
}
}
在测试配置了 Java 9 个模块的多模式 Spring 引导应用程序时,如何减轻 NoSuchBeanDefinitionException
和相关 UnsatisfiedDependencyException
的发生?
将 module-info.java
个文件添加到我的多模块项目后,我的 Spring 引导应用程序测试开始失败:
测试失败
contextLoads Time elapsed: 0 s <<< ERROR!
java.lang.IllegalStateException: Failed to load ApplicationContext
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'second':
Unsatisfied dependency expressed through constructor parameter 0;
nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'com.example.first.First' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type 'com.example.first.First' available:
expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
文件夹结构
parent
|
+ pom.xml
|
+-- first
| + pom.xml
| + src/main/java/module-info.java
| + src/main/java/com.example.first/First.java
|
+-- second
| + pom.xml
| + src/main/java/module-info.java
| + src/main/java/com.example.second/ApplicationConfig.java
| + src/main/java/com.example.second/Second.java
| + src/test/java/com.example.second/SecondTest.java
父模块
仅包含父 pom 文件的 Maven 父模块。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.6.2</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>parent</artifactId>
<version>0.1.0-SNAPSHOT</version>
<packaging>pom</packaging>
<properties>
<java.version>11</java.version>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>first</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.example</groupId>
<artifactId>second</artifactId>
<version>${project.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
<modules>
<module>first</module>
<module>second</module>
</modules>
</project>
第一个模块
一个简单的 Java 模块,它只包含一个 Java class 并且确实有任何 Spring 依赖项。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>parent</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>first</artifactId>
</project>
First.java
package com.example.first;
public class First {
}
第一个模块-info.java
module com.example.first {
exports com.example.first;
opens com.example.first;
}
第二个模块
除了所需的 Spring 依赖项之外,还依赖于第一个模块的应用程序模块。它负责配置 Spring bean、运行 应用程序等
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.example</groupId>
<artifactId>parent</artifactId>
<version>0.1.0-SNAPSHOT</version>
</parent>
<artifactId>second</artifactId>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>com.example</groupId>
<artifactId>first</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-engine</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.junit.jupiter</groupId>
<artifactId>junit-jupiter-api</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
Second.java
package com.example.second;
import com.example.first.First;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Second {
private final First first;
public Second(First first) {
this.first = first;
}
public static void main(String[] args) {
SpringApplication.run(ApplicationConfig.class, args);
System.out.println("started");
}
}
ApplicationConfig.java
package com.example.second;
import com.example.first.First;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ApplicationConfig {
@Bean
First first() {
return new First();
}
}
SecondTest.java
package com.example.second;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SecondTest {
@Test
void contextLoads() {
}
}
观察
- 无论我是否使用 Maven,测试都会失败,例如
mvn test
或者如果我尝试从我的 IDE. 中调试它们
- 可以从 IDE 以及使用
java -jar second/target/second-0.1.0-SNAPSHOT.jar
的命令行启动调用Second#main()
的应用程序,因此依赖注入和自动连接在此工作案例(在这种情况下,jar 文件必须在没有测试的情况下构建,因为它们失败了,例如mvn package -DskipTests=true
)所以First
bean 在这种情况下被正确创建和自动装配。 - 如果
module-info.java
个文件都被删除,则测试通过,所以错误似乎与 Java 个模块有关
根据@xerx593 评论中的反馈,我实施了一个解决方法,我将 @SpringBootApplication
class 提取到单独的 Application
class 并相应地更新了测试:
|
+-- second
| + pom.xml
| + src/main/java/module-info.java
| + src/main/java/com.example.second/Application.java
| + src/main/java/com.example.second/ApplicationConfig.java
| + src/main/java/com.example.second/Second.java
| + src/test/java/com.example.second/ApplicationTest.java
Application.java
package com.example.second;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
ApplicationConfig.java
package com.example.second;
import com.example.first.First;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ApplicationConfig {
@Bean
First first() {
return new First();
}
}
Second.java
package com.example.second;
import com.example.first.First;
import org.springframework.stereotype.Service;
@Service
public class Second {
private final First first;
public Second(First first) {
this.first = first;
}
}
ApplicationTest.java
package com.example.second;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest(classes = Application.class)
class ApplicationTest {
@Test
void contextLoads() {
}
}