我有一个带有REST控制器的Spring启动应用程序,一些服务和一些存储库。但是当我尝试启动时,Spring启动说:
java.lang.IllegalStateException: Failed to load ApplicationContext
at
org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:124)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:83)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:117)
at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
at org.springframework.boot.test.autoconfigure.SpringBootDependencyInjectionTestExecutionListener.prepareTestInstance(SpringBootDependencyInjectionTestExecutionListener.java:44)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:230)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:228)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:287)
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:289)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:247)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
at org.apache.maven.surefire.junit4.JUnit4Provider.execute(JUnit4Provider.java:283)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeWithRerun(JUnit4Provider.java:173)
at org.apache.maven.surefire.junit4.JUnit4Provider.executeTestSet(JUnit4Provider.java:153)
at org.apache.maven.surefire.junit4.JUnit4Provider.invoke(JUnit4Provider.java:128)
at org.apache.maven.surefire.booter.ForkedBooter.invokeProviderInSameClassLoader(ForkedBooter.java:203)
at org.apache.maven.surefire.booter.ForkedBooter.runSuitesInProcess(ForkedBooter.java:155)
at org.apache.maven.surefire.booter.ForkedBooter.main(ForkedBooter.java:103)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'terminController': Unsatisfied dependency expressed through field 'terminService'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'eu.wiegandt.toepferportal.services.TerminService' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
我有以下包结构:
eu.wiegandt.toepferportal.ToepferportalApplication
eu.wiegandt.toepferportal.controllers.TerminController
eu.wiegandt.toepferportal.services.TerminService
eu.wiegandt.toepferportal.services.TerminServiceImpl
当我理解Spring Boot(1.5.10)之后,它应该搜索从我的应用程序传出的所有子包中的bean。那么为什么我会得到这个例外呢?
这是我的课程:
应用:
package eu.wiegandt.toepferportal;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.scheduling.annotation.EnableScheduling;
import com.fasterxml.jackson.databind.Module;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
@SpringBootApplication
@EnableScheduling
public class ToepferportalApplication {
public static void main(final String[] args) {
SpringApplication.run(ToepferportalApplication.class, args);
}
@Bean
public Module javaTimeModule() {
return new JavaTimeModule();
}
@Bean
public Module jdk8Module() {
return new Jdk8Module();
}
}
控制器:
package eu.wiegandt.toepferportal.controllers;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import eu.wiegandt.toepferportal.entities.Termin;
import eu.wiegandt.toepferportal.services.TerminService;
@RestController
@RequestMapping("/termine")
public class TerminController {
@Autowired
private TerminService terminService;
@RequestMapping(method = RequestMethod.POST)
public Termin createTermin(@RequestBody final Termin termin) {
return terminService.saveOrUpdate(termin);
}
@RequestMapping(method = RequestMethod.GET)
public List<Termin> getAll() {
return terminService.listAll();
}
@ExceptionHandler
void handleIllegalArgumentException(final IllegalArgumentException e,
final HttpServletResponse response) throws IOException {
response.sendError(HttpStatus.BAD_REQUEST.value());
}
}
服务:
package eu.wiegandt.toepferportal.services;
import java.time.LocalDate;
import java.util.List;
import eu.wiegandt.toepferportal.entities.Termin;
public interface TerminService {
public void delete(Long id);
public List<Termin> findTerminForDay(LocalDate day);
public Termin getById(Long id);
public List<Termin> listAll();
public Termin saveOrUpdate(Termin termin);
}
服务Impl。:
package eu.wiegandt.toepferportal.services;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import eu.wiegandt.toepferportal.entities.Termin;
import eu.wiegandt.toepferportal.repositories.TerminRepository;
@Service
public class TerminServiceImpl implements TerminService {
@Autowired
private TerminRepository terminRepository;
@Override
public void delete(final Long id) {
terminRepository.delete(id);
}
@Override
public List<Termin> findTerminForDay(final LocalDate aDay) {
return terminRepository.findByBeginDate(aDay);
}
@Override
public Termin getById(final Long id) {
return terminRepository.findOne(id);
}
@Override
public List<Termin> listAll() {
final List<Termin> termine = new ArrayList<>();
terminRepository.findAll().forEach(termine::add);
return termine;
}
@Override
public Termin saveOrUpdate(final Termin termin) {
return terminRepository.save(termin);
}
}
用我开始整个事情的测试:
package eu.wiegandt.toepferportal;
import static org.mockito.BDDMockito.given;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
import java.util.ArrayList;
import java.util.List;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.http.MediaType;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import com.fasterxml.jackson.databind.ObjectMapper;
import eu.wiegandt.toepferportal.controllers.TerminController;
import eu.wiegandt.toepferportal.entities.Termin;
@RunWith(SpringRunner.class)
@WebMvcTest(TerminController.class)
@AutoConfigureMockMvc(secure = false)
public class TerminControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void testGetAll() throws Exception {
final List<Termin> testTermine = new ArrayList<>();
final ObjectMapper objectMapper = new ObjectMapper();
mvc.perform(get("/termine").accept(MediaType.APPLICATION_JSON_UTF8)).andExpect(status().isOk())
.andExpect(content().json(objectMapper.writeValueAsString(testTermine)));
}
}
我的maven pom:
<?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="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>eu.wiegandt</groupId>
<artifactId>toepferportal</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>toepferportal</name>
<description>A application to manag Termine for the TöWe.</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.10.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<test.db.root.pw>root</test.db.root.pw>
<test.db.name>toepferplaner-test</test.db.name>
<test.db.user.name>toepferplaner</test.db.user.name>
<test.db.user.pw>toepferplaner</test.db.user.pw>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jersey</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.mariadb.jdbc</groupId>
<artifactId>mariadb-java-client</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>com.spotify</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.23.0</version>
<configuration>
<imageName>${project.groupId}/${project.artifactId}</imageName>
<tag>${project.version}</tag>
<dockerDirectory>src/main/docker</dockerDirectory>
<resources>
<resource>
<targetPath>/</targetPath>
<directory>${project.build.directory}</directory>
<include>${project.build.finalName}.jar</include>
</resource>
</resources>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-failsafe-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>integration-test</goal>
<goal>verify</goal>
</goals>
</execution>
</executions>
<configuration>
<environmentVariables>
<TOEPFERPLANER_DB_HOST>localhost</TOEPFERPLANER_DB_HOST>
<TOEPFERPLANER_DB_PORT>${it-database.port}</TOEPFERPLANER_DB_PORT>
<TOEPFERPLANER_DB_DATABASE>${test.db.name}</TOEPFERPLANER_DB_DATABASE>
<TOEPFERPLANER_DB_USER>${test.db.user.name}</TOEPFERPLANER_DB_USER>
<TOEPFERPLANER_DB_PASSWORD>${test.db.user.pw}</TOEPFERPLANER_DB_PASSWORD>
</environmentVariables>
</configuration>
</plugin>
<plugin>
<groupId>io.fabric8</groupId>
<artifactId>docker-maven-plugin</artifactId>
<version>0.20.1</version>
<executions>
<execution>
<id>prepare-it-database</id>
<phase>pre-integration-test</phase>
<goals>
<goal>start</goal>
</goals>
<configuration>
<images>
<image>
<name>mariadb:latest</name>
<alias>it-database</alias>
<run>
<ports>
<port>it-database.port:3306</port>
</ports>
<env>
<MYSQL_ROOT_PASSWORD>${test.db.root.pw}</MYSQL_ROOT_PASSWORD>
<MYSQL_DATABASE>${test.db.name}</MYSQL_DATABASE>
<MYSQL_USER>${test.db.user.name}</MYSQL_USER>
<MYSQL_PASSWORD>${test.db.user.pw}</MYSQL_PASSWORD>
</env>
<wait>
<log>mysqld: ready for connections</log>
<time>20000</time>
</wait>
</run>
</image>
</images>
</configuration>
</execution>
<execution>
<id>remove-it-database</id>
<phase>post-integration-test</phase>
<goals>
<goal>stop</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/docker</directory>
<filtering>true</filtering>
</resource>
</resources>
</build>
</project>
我可以解决我的问题,在我的测试中用@WebMvcTest(TerminController.class)
替换@SpringBootTest
。