
micronaut 应用中控制器返回“not found”,根本原因通常是组件未被正确注册——这源于 micronaut 编译时依赖注入机制的严格性:类必须显式标注作用域注解、包路径需被主应用类覆盖,且构建配置须启用正确的运行时和注解处理。
micronaut 应用中控制器返回“not found”,根本原因通常是组件未被正确注册——这源于 micronaut 编译时依赖注入机制的严格性:类必须显式标注作用域注解、包路径需被主应用类覆盖,且构建配置须启用正确的运行时和注解处理。
Micronaut 与 Spring Boot 的核心差异在于其编译期 Bean 注册机制:它不会自动扫描所有 @Controller 类,而是依赖注解处理器在编译阶段生成元数据(如 META-INF/micronaut/beans.ser)。若配置或代码任一环节缺失,控制器将完全不可见,导致所有路由返回 404 Not Found —— 这正是你遇到的现象。
✅ 关键修复点详解
1. 主应用类位置与包路径覆盖
确保 Application 类位于 com.micronaut 包下(即与 com.micronaut.broker 同级或父包),且使用 @MicronautApplication 注解:
// src/main/java/com/micronaut/Application.java
package com.micronaut;
import io.micronaut.runtime.Micronaut;
public class Application {
public static void main(String[] args) {
Micronaut.run(Application.class, args);
}
}
⚠️ 注意:@MicronautApplication 是可选但推荐的(它隐含 @Context 和默认配置)。若未显式添加,需确认 Micronaut.run() 启动的是该类,且其包路径能覆盖 com.micronaut.broker(即 com.micronaut 是 com.micronaut.broker 的父包)。
2. Controller 必须标注 @Singleton(或其它作用域)
Micronaut 要求所有可注入组件(包括 @Controller)必须声明作用域。仅 @Controller 不足,需叠加 @Singleton:
package com.micronaut.broker;
import io.micronaut.http.annotation.Controller;
import io.micronaut.http.annotation.Get;
import jakarta.inject.Singleton; // ← 关键!使用 jakarta.inject,非 javax.inject
@Singleton // ← 必须添加!否则不被注册为 Bean
@Controller("/symbol")
public class SymbolController {
private final InMemoryStore inMemoryStore;
public SymbolController(InMemoryStore inMemoryStore) {
this.inMemoryStore = inMemoryStore;
}
@Get("/")
public List<symbol> getAll() {
return new ArrayList(inMemoryStore.getSymbols().values());
}
@Get("/hello")
public String hello() {
return "hello";
}
}</symbol>
? 验证:检查 build/classes/java/main/META-INF/micronaut/ 下是否存在 beans.ser 或 beans.xml(若启用注解处理器),这是 Micronaut 扫描成功的标志。
3. Gradle 构建配置修正
你当前的 build.gradle 存在多个关键问题,已优化如下(适配 Micronaut 3.7.9 + JDK 17):
plugins {
id("io.micronaut.application") version "3.7.9"
id("com.github.johnrengelman.shadow") version "7.1.2" apply false // Shadow 插件建议不自动应用
}
version = "0.1"
group = "com.micronaut"
repositories {
mavenCentral()
}
dependencies {
implementation("io.micronaut:micronaut-http-client")
implementation("io.micronaut:micronaut-jackson-databind")
implementation("io.micronaut:micronaut-http-validation") // annotationProcessor 已弃用,改用 implementation
implementation("jakarta.annotation:jakarta.annotation-api")
// 移除 javax.inject:Micronaut 3+ 全面迁移到 Jakarta EE 9+
// runtimeOnly("io.micronaut:micronaut-http-server-netty") // 由 micronaut.runtime 自动引入
runtimeOnly("ch.qos.logback:logback-classic")
implementation("net.datafaker:datafaker:1.9.0")
}
application {
mainClass.set("com.micronaut.Application")
}
java {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
micronaut {
runtime("netty") // ← 必须显式声明运行时,否则无内嵌服务器
testRuntime("junit5")
processing {
incremental(true)
annotations("com.micronaut.*")
}
}
? 提示:micronaut.runtime("netty") 是关键——它会自动引入 micronaut-http-server-netty 及相关 Netty 依赖,无需手动声明 runtimeOnly,避免版本冲突。
4. 启动与调试验证步骤
-
命令行启动:执行 ./gradlew run,观察控制台是否输出类似:
INFO i.m.h.s.n.NettyHttpServer - Starting Netty HTTP Server on http://localhost:8080 INFO i.m.c.e.BeanDefinitionInjectProcessor - Registered 1 bean definition for type [com.micronaut.broker.SymbolController]
- 断点生效:确保在 IDE(如 IntelliJ)中使用 Gradle Task → run 启动(而非直接运行 main()),否则注解处理器可能未触发,beans.ser 不生成,断点自然不命中。
- 健康检查:访问 http://localhost:8080/health,若返回 {"status":"UP"},说明服务已启动;再试 http://localhost:8080/symbol/hello。
? 常见误区排查清单
| 问题 | 检查项 |
|---|---|
| ❌ 路由 404 | Controller 是否在 @MicronautApplication 扫描路径内?包名是否匹配? |
| ❌ 断点不触发 | 是否通过 ./gradlew run 或 IDE 的 Gradle task 启动?直接 Run main class 会跳过注解处理 |
| ❌ No bean of type [X] exists | InMemoryStore 是否也标注了 @Singleton?构造函数注入依赖必须是可发现的 Bean |
| ❌ 构建失败/依赖冲突 | 删除 build/ 目录和 ~/.gradle/caches/ 中对应模块缓存后重试 |
✅ 总结
Micronaut 的“约定优于配置”实为“显式优于隐式”:每个可注入组件都需明确声明生命周期(@Singleton)、每个运行时需主动选择(micronaut.runtime("netty"))、每个构建环节需保障注解处理启用。修复上述三点后,SymbolController 将被正确注册,/symbol/hello 端点即可正常响应。记住:不是 Micronaut “找不到”控制器,而是它根本没把它当作 Bean 注册进来——这是设计使然,而非 bug。











