Spring Boot Autowired 存储库不适用于 SpringRunner 和 JPA 数据

Posted

技术标签:

【中文标题】Spring Boot Autowired 存储库不适用于 SpringRunner 和 JPA 数据【英文标题】:Spring boot Autowired repository not working with SpringRunner and JPA data 【发布时间】:2017-11-13 20:20:32 【问题描述】:

当我尝试运行我的测试时,我遇到了错误。我是 Spring Boot 新手,所以无法理解确切的问题。

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'api.usecase.createtask.CreateTaskTest': Unsatisfied dependency expressed through field 'taskRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'api.usecase.createtask.repository.CreateTaskRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: @org.springframework.beans.factory.annotation.Autowired(required=true)

    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:588)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:386)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:118)
    at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:83)
    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.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:51)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'api.usecase.createtask.repository.CreateTaskRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: @org.springframework.beans.factory.annotation.Autowired(required=true)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1486)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:585)
    ... 27 more

我的单元测试

package api.usecase.createtask;

import api.model.Task;
import api.usecase.createtask.contracts.CreateTaskRequest;
import api.usecase.createtask.contracts.CreateTaskResponse;
import api.usecase.createtask.repository.CreateTaskRepository;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.List;

@RunWith(SpringRunner.class)
public class CreateTaskTest 

    @Autowired
    private CreateTaskRepository taskRepository;

    @Test
    public void createSuccessfulTask() 
        CreateTaskRequest createTaskRequest = new CreateTaskRequest();
        createTaskRequest.setDescription("Wash car");
        createTaskRequest.setAmount(10);
        createTaskRequest.setRecurrenceType("ONE_OFF");
        createTaskRequest.setStatus("NOT_COMPLETED");
        CreateTaskResponse createTaskResponse = new CreateTaskResponse();
        CreateTask createTask = new CreateTask(this.taskRepository);
        createTask.handle(createTaskRequest, createTaskResponse);
        List<Task> tasks = this.taskRepository.findAll();
        tasks.forEach(System.out::println);
    

我的仓库类

package api.usecase.createtask.repository;


import api.model.Task;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;

@Repository
public interface CreateTaskRepository extends JpaRepository <Task, Long>
    List<Task> findAll();

主应用程序文件。我还尝试了 @EnableJpaRepositories(basePackages = "api.usecase.createtask.repository", ConsiderNestedRepositories = true) 选项,但仍然无法正常工作。

package api;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application 

    public static void main(String[] args) 
        SpringApplication.run(Application.class, args);
    

创建任务逻辑类

package api.usecase.createtask;

import api.model.Task;
import api.usecase.createtask.contracts.CreateTaskRequest;
import api.usecase.createtask.contracts.CreateTaskResponse;
import api.usecase.createtask.repository.CreateTaskRepository;


public class CreateTask 
    private CreateTaskRepository repository;

    public CreateTask(CreateTaskRepository repository) 
        this.repository = repository;
    

    public void handle(CreateTaskRequest createTaskRequest, CreateTaskResponse createTaskResponse) 
        Task task = new Task(
                createTaskRequest.getDescription(),
                createTaskRequest.getAmount(),
                createTaskRequest.getRecurrenceType(),
                createTaskRequest.getStatus()
        );
        this.repository.save(task);
    

型号

package api.model;

import org.springframework.stereotype.Component;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;

@Entity
@Table(name = "CHILD_Tasks")
public class Task 
    @Id
    @Column(name = "id")
    private long id;

    @Column(name = "description")
    private String description;

    @Column(name = "amount")
    private float amount;

    @Column(name = "recurrenceType")
    private String recurrenceType;

    @Column(name = "status")
    private String status;

    public Task(String description, Float amount, String recurrenceType, String status) 
        this.description = description;
        this.amount = amount;
        this.recurrenceType = recurrenceType;
        this.status = status;
    

    public void setId(long id) 
        this.id = id;
    

    public long getId() 
        return this.id;
    

    public void setDescription(String description) 
        this.description = description;
    

    public String getDescription() 
        return this.description;
    

    public void setAmount(float amount) 
        this.amount = amount;
    

    public float getAmount() 
        return this.amount;
    

    public void setRecurrenceType(String recurrenceType) 
        this.recurrenceType = recurrenceType;
    

    public String getRecurrenceType() 
        return this.recurrenceType;
    

    public void setStatus(String status) 
        this.status = status;
    

    public String getStatus() 
        return this.status;
    

目录结构

应用程序属性文件

spring.datasource.url=jdbc:mysql://localhost/tasks_db
spring.datasource.username=root
spring.datasource.password=root

spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql = true
spring.jpa.hibernate.naming-strategy = org.hibernate.cfg.ImprovedNamingStrategy
spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL5Dialect

spring.h2.console.enabled=true
spring.h2.console.path=/console
spring.h2.console.settings.trace=false
spring.h2.console.settings.web-allow-others=false

Maven 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="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.example</groupId>
    <artifactId>api</artifactId>
    <version>2.0</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>

        <dependency>
            <groupId>io.jsonwebtoken</groupId>
            <artifactId>jjwt</artifactId>
            <version>0.7.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-java</artifactId>
            <version>$cucumber.version</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-spring</artifactId>
            <version>$cucumber.version</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>info.cukes</groupId>
            <artifactId>cucumber-junit</artifactId>
            <version>$cucumber.version</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.h2database</groupId>
            <artifactId>h2</artifactId>
            <version>1.4.194</version>
        </dependency>
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-entitymanager</artifactId>
            <version>5.2.9.Final</version>
        </dependency>

    </dependencies>

    <properties>
        <java.version>1.8</java.version>
        <cucumber.version>1.2.5</cucumber.version>
    </properties>

    <distributionManagement>
        <snapshotRepository>
            <id>snapshots</id>
            <url>http://0.0.0.0:8081/nexus/content/repositories/snapshots</url>
        </snapshotRepository>
        <repository>
            <id>releases</id>
            <url>http://0.0.0.0:8081/nexus/content/repositories/releases</url>
        </repository>
    </distributionManagement>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-deploy-plugin</artifactId>
                <configuration>
                    <skip>true</skip>
                </configuration>
            </plugin>
            <plugin>
                <groupId>org.sonatype.plugins</groupId>
                <artifactId>nexus-staging-maven-plugin</artifactId>
                <version>1.6.3</version>
                <executions>
                    <execution>
                        <id>default-deploy</id>
                        <phase>deploy</phase>
                        <goals>
                            <goal>deploy</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <serverId>nexus</serverId>
                    <nexusUrl>http://0.0.0.0:8081/nexus/</nexusUrl>
                    <skipStaging>true</skipStaging>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

【问题讨论】:

如果您将程序作为启动应用程序启动,是否也会发生这种情况? 我没有检查过。我只是运行我的测试。 我收到 java.lang.IllegalArgumentException:必须至少存在一个 JPA 元模型!运行应用程序时出错。 @shivanshupatel 尝试删除 spring-data-jpa 的版本声明 尝试了创建名称为“jpaMappingContext”的 bean 时出错:调用 init 方法失败;嵌套异常是 java.lang.IllegalArgumentException:必须存在至少一个 JPA 元模型!错误。谢谢 【参考方案1】:

您尚未指定您的 Spring 配置。 Spring 不知道你有什么豆子,也不知道在哪里可以找到它们。

如果您只想测试存储库,则需要添加 DataJpaTest 注释。文档:http://docs.spring.io/spring-boot/docs/current/api/org/springframework/boot/test/autoconfigure/orm/jpa/DataJpaTest.html

@RunWith(SpringRunner.class)
@DataJpaTest
@AutoConfigureTestDatabase(connection = EmbeddedDatabaseConnection.H2)
public class CreateTaskTest 
...

默认情况下,它将配置一个内存嵌入式数据库,扫描 @Entity 类并配置 Spring Data JPA 存储库。常规的 @Component beans 不会被加载到 ApplicationContext 中。

数据 JPA 测试是事务性的,并在每次测试结束时回滚 默认情况下 [...]

Data JPA 测试也可以注入一个 TestEntityManager bean,它提供 专门设计的标准 JPA EntityManager 的替代方案 用于测试。如果您想在外部使用 TestEntityManager @DataJpaTests 你也可以使用 @AutoConfigureTestEntityManager 注解。 [...]

@RunWith(SpringRunner.class) @DataJpaTest public class ExampleRepositoryTests 

    @Autowired
    private TestEntityManager entityManager;

    @Autowired
    private UserRepository repository;

    @Test
    public void testExample() throws Exception 
        this.entityManager.persist(new User("sboot", "1234"));
        User user = this.repository.findByUsername("sboot");
        assertThat(user.getUsername()).isEqualTo("sboot");
        assertThat(user.getVin()).isEqualTo("1234");
     

此外,如果您需要从所有组件创建 bean,您可以使用 SpringBootTest

我建议您阅读 Spring Boot 测试参考 (link)。 Spring 有非常好的文档。

【讨论】:

添加 JPADataTest 和 TestEntityManager 后,我得到了原因:java.lang.IllegalArgumentException:必须存在至少一个 JPA 元模型!错误。您能给我链接以查找有关弹簧配置的更多信息吗?是的,我没有添加它。 @shivanshupatel 添加 spring data jpa 依赖。 mvnrepository.com/artifact/org.springframework.boot/… @shivanshupatel 这里是关于使用 Spring Boot 进行测试的完整文档:docs.spring.io/spring-boot/docs/current/reference/html/… 谢谢,我已经在 POM 文件中找到了,不知道为什么不工作 @shivanshupatel 尝试运行mvn clean install。此外,您可以将您的 pom 添加到问题中

以上是关于Spring Boot Autowired 存储库不适用于 SpringRunner 和 JPA 数据的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot - DI - 单元测试

在春季启动autowired不工作,总是null?

spring boot 如何将没有注解的类@Autowired

如何在 Spring Boot 中手动新建实例中使用 @Autowired

spring boot filter -Autowired

如何在 Spring Boot 类之间正确注入 @Autowired?