注解在spring容器启动时的beandefinition注册阶段被反射读取并解析,而非实例化或初始化时生效;@component等由classpathbeandefinitionscanner扫描,@configuration类中的@bean由configurationclasspostprocessor解析,所有注解均通过反射静态分析并影响beandefinition属性,不参与运行时逻辑。

注解本身不参与运行时逻辑,它在Spring容器启动过程中只在BeanDefinition注册阶段被读取和解析,而非在实例化或初始化时才“生效”。关键不是“什么时候执行注解”,而是“什么时候扫描并转换成BeanDefinition”。
注解扫描发生在容器刷新早期
Spring容器(如ApplicationContext)调用refresh()后,第一步就是加载配置元数据。此时:
- @Component、@Service等组件注解,由
ClassPathBeanDefinitionScanner扫描类路径下的class文件,并通过反射读取其上的注解信息; - @Configuration类中的@Bean方法,由
ConfigurationClassPostProcessor(一个BeanDefinitionRegistryPostProcessor)在postProcessBeanDefinitionRegistry阶段解析; - 所有注解读取都基于Java反射的
Class.getAnnotations()或Method.getAnnotation(),发生在Bean实例创建之前,纯静态分析过程。
@Lazy、@Conditional等注解影响的是BeanDefinition的属性,不是运行时行为
这些注解不会改变反射读取时机,但会修改生成的BeanDefinition对象内部字段:
- @Lazy设置
beanDefinition.setLazyInit(true),影响后续是否在预热阶段实例化; - @Conditional及其衍生注解(如@ConditionalOnMissingBean)在
ConfigurationClassPostProcessor解析@Bean方法时就完成条件评估,不满足则跳过该BeanDefinition注册; - @Scope、@Primary等同理,均写入BeanDefinition,供后续实例化流程决策使用。
反射读取注解不等于Bean创建,更不等于初始化
很多人混淆“读到注解”和“执行注解效果”。实际上:
- 反射读取注解 → 发生在容器启动
invokeBeanFactoryPostProcessors阶段,早于任何Bean实例化; - 注解触发的行为(如@Autowired注入、@PostConstruct执行)→ 发生在单个Bean的初始化流程中,属于第二阶段;
- 同一个类上多个注解(如@Component + @Lazy + @Scope)会被一次性读取,但各自作用于生命周期不同环节。
自定义注解需配合处理器才能生效
如果你写了自定义注解(如@RemoteService),它不会自动被Spring识别。必须:
- 编写
BeanDefinitionRegistryPostProcessor或ImportBeanDefinitionRegistrar,在容器早期通过反射检测该注解并手动注册BeanDefinition; - 或提供
BeanPostProcessor在初始化前后检查目标Bean是否标注该注解并做增强; - 单纯加注解而不注册处理器,Spring启动时完全忽略它——反射读取了,但没人处理。











