SpringBootTest中无法使用构造注入?求原因解析及替代方案
@SpringBootTest (and How to Fix It) Hey there! Let's tackle your question about constructor injection in Spring Boot tests. First, let's break down why you can't use plain constructor injection with @SpringBootTest out of the box, then go over the ways you can make it work.
Why Constructor Injection Fails Here
When you use @SpringBootTest, Spring loads your full application context, but your test class itself isn't a Spring-managed bean by default. Normally, constructor injection works for Spring beans because Spring is responsible for instantiating them and resolving dependencies through their constructors. But for test classes, the test framework (JUnit) creates the instance first, and Spring Test then injects dependencies via @Autowired fields or methods using reflection. That's why your initial attempt to use constructor injection without extra setup doesn't work.
How to Use Constructor Injection with @SpringBootTest
Luckily, there are two straightforward ways to enable constructor injection in your @SpringBootTest classes:
1. Add @Autowired to Your Test Class Constructor
You can explicitly mark your test class's constructor with @Autowired. Spring Test will recognize this and use the constructor to inject all required dependencies, including special ones like @LocalServerPort. Here's how to modify your test code:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) class PersonControllerTest { private int port; private PersonController controller; private TestRestTemplate restTemplate; @Autowired public PersonControllerTest(@LocalServerPort int port, PersonController controller, TestRestTemplate restTemplate) { this.port = port; this.controller = controller; this.restTemplate = restTemplate; } @Test public void greetingShouldReturnDefaultMessage() throws Exception { assertThat(this.restTemplate.getForObject("http://localhost:" + port + "/cvtech/Persons/", String.class)).contains("content"); } @Test public void contextLoads() throws Exception { assertThat(controller).isNotNull(); } @Test void findAllByJob() { } }
2. Use Spring Boot 2.2+ with JUnit 5 (No @Autowired Needed)
If you're using Spring Boot 2.2 or later along with JUnit Jupiter (JUnit 5), you can skip the @Autowired annotation entirely. Spring Boot's test support automatically detects and uses the test class's constructor to inject dependencies. This works because Spring Test integrates with JUnit 5's extension model to handle constructor injection for test classes. Your code would look like this:
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT) class PersonControllerTest { private int port; private PersonController controller; private TestRestTemplate restTemplate; // No @Autowired needed here! public PersonControllerTest(@LocalServerPort int port, PersonController controller, TestRestTemplate restTemplate) { this.port = port; this.controller = controller; this.restTemplate = restTemplate; } // Test methods remain the same... }
Quick Notes
- Make sure all dependencies you're injecting (like
PersonControllerandTestRestTemplate) are present in the application context loaded by@SpringBootTest. TestRestTemplateis auto-configured by Spring Boot when using@SpringBootTestwith a web environment, so it's safe to inject directly.
内容的提问来源于stack exchange,提问作者BARHOUMI

