Spring Boot无法找到Repository Bean问题求助
Spring Boot + Spring Data JDBC 启动报错:无法找到TodoRepository Bean
错误信息
Parameter 0 of constructor in dev.iosenberg.todo.controllers.TodoController required a bean of type 'dev.iosenberg.todo.repositories.TodoRepository' that could not be found.
相关代码
TodoApplication.java
package dev.iosenberg.todo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class TodoApplication { public static void main(String[] args) { SpringApplication.run(TodoApplication.class, args); } }
models/Todo.java
package dev.iosenberg.todo.models; import org.springframework.data.annotation.Id; public record Todo(@Id Long id, Long userId, String name, String description, boolean completed) { }
repositories/TodoRepository.java
package dev.iosenberg.todo.repositories; import org.springframework.data.repository.CrudRepository; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.stereotype.Repository; import dev.iosenberg.todo.models.Todo; @Repository public interface TodoRepository extends CrudRepository<Todo,Long>, PagingAndSortingRepository<Todo, Long>{ }
controllers/TodoController.java
package dev.iosenberg.todo.controllers; import java.net.URI; import java.util.Optional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.autoconfigure.data.web.SpringDataWebProperties.Pageable; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.util.UriComponentsBuilder; import dev.iosenberg.todo.models.Todo; import dev.iosenberg.todo.repositories.TodoRepository; import java.util.List; @RestController @RequestMapping("/todos") public class TodoController { @Autowired private TodoRepository todoRepository; public TodoController(TodoRepository todoRepository) { this.todoRepository = todoRepository; } @GetMapping public ResponseEntity<List<Todo>> findAll(Pageable pageable) { Page<Todo> page = todoRepository.findAll( PageRequest.of(1,1) ); return ResponseEntity.ok(page.getContent()); } @GetMapping("/{requestedId}") public ResponseEntity<Todo> findById(@PathVariable Long requestedId) { Optional<Todo> todoOptional = todoRepository.findById(requestedId); if(todoOptional.isPresent()) { return ResponseEntity.ok(todoOptional.get()); } else { return ResponseEntity.notFound().build(); } } @PostMapping public ResponseEntity<Void> createTodo(@RequestBody Todo newTodoRequest, UriComponentsBuilder ucb) { Todo savedTodo = todoRepository.save(newTodoRequest); URI locationOfNewTodo = ucb .path("todos/{id}") .buildAndExpand(savedTodo.id()) .toUri(); return ResponseEntity.created(locationOfNewTodo).build(); } @PutMapping("/{id}") private ResponseEntity<Void> putTodo(@PathVariable Long id, @RequestBody Todo todoUpdate) { Optional<Todo> todoOptional = todoRepository.findById(id); if(todoOptional.isPresent()) { Todo updatedTodo = todoUpdate; todoRepository.save(updatedTodo); return ResponseEntity.noContent().build(); } return ResponseEntity.notFound().build(); } @DeleteMapping("/{id}") private ResponseEntity<Void> deleteTodo(@PathVariable Long id) { if(!todoRepository.existsById(id)) { return ResponseEntity.notFound().build(); } todoRepository.deleteById(id); return ResponseEntity.noContent().build(); } }
MvcConfig.java
package dev.iosenberg.todo; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.ViewControllerRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class MvcConfig implements WebMvcConfigurer { public void addViewControllers(ViewControllerRegistry registry) { registry.addViewController("/home").setViewName("home"); registry.addViewController("/").setViewName("home"); registry.addViewController("/test").setViewName("test"); registry.addViewController("/hello").setViewName("hello"); registry.addViewController("/login").setViewName("login"); } }
build.gradle
plugins { id 'java' id 'org.springframework.boot' version '3.1.5' id 'io.spring.dependency-management' version '1.1.3' } group = 'dev.iosenberg' version = '0.0.1-SNAPSHOT' java { sourceCompatibility = '17' } repositories { mavenCentral() } dependencies { // implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-thymeleaf' implementation 'org.springframework.boot:spring-boot-starter-web' // implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6' implementation 'org.springframework.data:spring-data-jdbc' testImplementation 'com.h2database:h2' testImplementation 'org.springframework.boot:spring-boot-starter-test' // testImplementation 'org.springframework.security:spring-security-test' } tasks.named('test') { useJUnitPlatform() } test { testLogging { events "passed", "skipped", "failed", "standardOut", "standardError" showExceptions true exceptionFormat "full" showCauses true showStackTraces true // Change to `true` for more verbose test output showStandardStreams = true } }
控制台完整输出
2023-11-09T12:09:23.449-05:00 INFO 27748 --- [ main] dev.iosenberg.todo.TodoApplication : Starting TodoApplication using Java 17.0.9 with PID 27748 (C:\Users\ikeos\Documents\git\todo\bin\main started by ikeos in C:\Users\ikeos\Documents\git\todo) 2023-11-09T12:09:23.455-05:00 INFO 27748 --- [ main] dev.iosenberg.todo.TodoApplication : No active profile set, falling back to 1 default profile: "default" 2023-11-09T12:09:25.106-05:00 INFO 27748 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http) 2023-11-09T12:09:25.119-05:00 INFO 27748 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat] 2023-11-09T12:09:25.120-05:00 INFO 27748 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.15] 2023-11-09T12:09:25.274-05:00 INFO 27748 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext 2023-11-09T12:09:25.277-05:00 INFO 27748 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 1740 ms 2023-11-09T12:09:25.366-05:00 WARN 27748 --- [ main] ConfigServletWebServerApplicationContext : Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'todoController' defined in file [C:\Users\ikeos\Documents\git\todo\bin\main\dev\iosenberg\todo\controllers\TodoController.class]: Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'dev.iosenberg.todo.repositories.TodoRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {} 2023-11-09T12:09:25.372-05:00 INFO 27748 --- [ main] o.apache.catalina.core.StandardService : Stopping service [Tomcat] 2023-11-09T12:09:25.395-05:00 INFO 27748 --- [ main] .s.b.a.l.ConditionEvaluationReportLogger : Error starting ApplicationContext. To display the condition evaluation report re-run your application with 'debug' enabled. 2023-11-09T12:09:25.431-05:00 ERROR 27748 --- [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPLICATION FAILED TO START *************************** Description: Parameter 0 of constructor in dev.iosenberg.todo.controllers.TodoController required a bean of type 'dev.iosenberg.todo.repositories.TodoRepository' that could not be found. Action: Consider defining a bean of type 'dev.iosenberg.todo.repositories.TodoRepository' in your configuration.
已尝试的解决方案
- 添加@ComponentScan指定基础包:消除了Repository Bean错误,但访问页面出现Whitelabel 404错误;
- 将测试资源下的data.sql和schema.sql移到src/main/resources:出现表不存在的SQL错误。
解决方案
1. 配置主环境数据源(以H2为例)
Spring Data JDBC需要数据源才能创建Repository代理Bean,测试环境自动配置了H2,但主环境没有。在build.gradle中添加H2的运行时依赖:
runtimeOnly 'com.h2database:h2'
然后在src/main/resources下创建application.properties,添加H2配置:
spring.datasource.url=jdbc:h2:mem:tododb spring.datasource.driverClassName=org.h2.Driver spring.datasource.username=sa spring.datasource.password= spring.h2.console.enabled=true spring.sql.init.mode=always
spring.sql.init.mode=always会自动执行src/main/resources下的schema.sql和data.sql初始化数据库。
2. 修正Controller注入方式
同时使用字段注入和构造注入会造成混淆,保留构造注入(Spring 4.3+无需@Autowired):
@RestController @RequestMapping("/todos") public class TodoController { private final TodoRepository todoRepository; public TodoController(TodoRepository todoRepository) { this.todoRepository = todoRepository; } // 其他方法保持不变 }
3. 移除多余的@ComponentScan
@SpringBootApplication已经包含@ComponentScan,默认扫描主类所在包(dev.iosenberg.todo)及所有子包,额外添加会导致扫描范围异常,引发404问题。
4. 确保数据库初始化文件存在
在src/main/resources下添加:
schema.sql
CREATE TABLE IF NOT EXISTS todo ( id BIGINT AUTO_INCREMENT PRIMARY KEY, user_id BIGINT, name VARCHAR(255) NOT NULL, description TEXT, completed BOOLEAN DEFAULT FALSE );
data.sql
INSERT INTO todo (user_id, name, description, completed) VALUES (1, 'Learn Spring Data JDBC', 'Master basics of Spring Data JDBC', false), (1, 'Build Todo App', 'Complete the todo application', false);
5. 验证页面访问
确保src/main/resources/templates下存在对应的Thymeleaf模板文件(如home.html),否则访问/home会出现404。示例home.html:
<!DOCTYPE html> <html xmlns:th="http://www.thymeleaf.org"> <head> <title>Home</title> </head> <body> <h1>Todo App Home</h1> <a href="/todos">View Todos API</a> </body> </html>
内容的提问来源于stack exchange,提问作者Ike Osenberg
相关产品推荐
相关产品推荐

