@Component 本身仅为标记,需配合@ComponentScan配置的扫描路径才能被Spring发现并注册为Bean;未配置或路径不匹配会导致注入失败。

@Component 是怎么被 Spring 找到并注册成 Bean 的
Spring 不会自动扫描所有类,@Component 本身不带“自动生效”属性,它只是个标记;真正起作用的是组件扫描(component scan)配置。没配扫描路径,加了 @Component 也白搭。
常见错误现象:NoSuchBeanDefinitionException,明明写了 @Component,却注入失败。
- 检查是否在启动类或配置类上加了
@ComponentScan,且 basePackages 覆盖目标类所在包 - 默认只扫描启动类所在包及其子包;如果类在
com.example.util,而启动类在com.example.app,那util包不会被扫到 - Maven 多模块项目中,
@ComponentScan不跨模块生效,不能指望父模块配置扫到子模块的类 - IDE 编译输出路径和 Spring Boot 的 classpath 加载顺序有时不一致,冷启动后改了注解没重启应用,也会“看似失效”
@Autowired 注入失败的几个真实原因
@Autowired 看似简单,但失败时往往不是“写错了”,而是上下文根本没准备好——比如在构造函数之外手动 new 出来的对象里用它,必然为空。
使用场景限制很明确:只能用于 Spring 容器管理的 Bean 内部,且字段/方法必须在 Bean 实例化后由容器回调注入。
立即学习“Java免费学习笔记(深入)”;
- 不要在
static字段或方法上用@Autowired—— Spring 不处理静态成员 - 避免在
@PostConstruct方法之前访问未注入的@Autowired字段,可能为 null(尤其配合懒加载或条件装配时) - 当存在多个同类型 Bean 时,
@Autowired会报错,必须配合@Qualifier或@Primary指定具体实例 - Lombok 的
@Data+@RequiredArgsConstructor会生成全参构造,若构造参数含未定义 Bean,会导致启动失败,而非注入失败
为什么有时候 @Autowired 字段是 null,但换成构造器注入就正常
字段注入(field injection)依赖反射设值,时机晚于构造器执行;而构造器注入(constructor injection)强制依赖在实例化时就必须就位。null 的本质,往往是对象创建早于依赖注入流程。
典型例子:在 @Configuration 类中,用 new XxxService() 创建对象,再给它的字段标 @Autowired —— 这个对象压根不在容器里,Spring 根本不会碰它。
- 构造器注入能清晰暴露依赖关系,也能避免循环依赖时的诡异行为(字段注入可能掩盖问题)
- Spring 5.3+ 对构造器注入做了优化,单构造器可省略
@Autowired注解;但多构造器时仍需显式标注 - 测试中 mock 依赖时,构造器注入更易替换,字段注入常要靠
ReflectionTestUtils强行设值 - 字段注入在单元测试里容易误以为“只要 new 出来就能跑”,结果集成时崩在 DI 阶段
@Component 和 @Service/@Repository/@Controller 有啥实际区别
语义不同,但底层都是 @Component 的派生注解;Spring 容器眼里它们等价,都能被 @ComponentScan 扫到。区别只在代码可读性和部分扩展场景。
性能、启动速度、内存占用完全没差异;但某些工具链或监控系统会识别这些分层注解做分类统计。
-
@Service推荐用于业务逻辑层,AOP 切面(如@Transactional)默认只对@Service生效(可通过配置改) -
@Repository会让 Spring 自动把数据访问异常转成DataAccessException层次结构,并支持统一异常处理 -
@Controller和@RestController是 Web 层专用,触发 MVC 相关后置处理器(如HandlerMethodReturnValueHandler) - 自定义注解若想继承
@Component行为,必须显式声明@Component,不能只靠元注解传递
复杂点在于:这些注解的语义约束是团队约定 + 工具提示,Spring 本身不强制校验层级调用关系。写反了(比如在 Controller 里直接 new Service),编译运行都 OK,但后期维护和测试成本会悄悄升高。










