加了@Configuration后Spring通过CGLIB代理确保@Bean方法返回容器中已有实例而非重复new对象;代理在ConfigurationClassPostProcessor阶段生成,重写@Bean方法实现先查缓存再创建。

为什么加了 @Configuration 就能避免重复创建 Bean
不加 @Configuration 的配置类,本质就是普通 Java 类,每次调用 @Bean 方法都等同于 new 一次对象;加了之后,Spring 会在启动时用 CGLIB 动态生成子类代理,把方法调用“重定向”到容器查找逻辑。关键不是“方法只执行一次”,而是“方法体内的 new 操作被绕过了”。
CGLIB 代理在哪个环节介入
代理发生在 ConfigurationClassPostProcessor 处理阶段,它属于 BeanFactoryPostProcessor,在所有 bean 定义加载完毕、但尚未实例化前触发。此时 Spring 扫描出所有带 @Configuration 的类,对它们做 CGLIB 增强——注意:这个代理对象本身是单例,且持有对 BeanFactory 的引用。
- 代理类继承原配置类,重写所有
@Bean方法 - 重写后的方法逻辑变成:先查
beanFactory.getSingleton(beanName),命中则直接返回 - 未命中才真正执行原方法体(即 new 对象 + 注册进 singletonObjects)
- 手动调用代理对象的
@Bean方法(如config.userService())也走这套逻辑
@Bean 方法间互相调用时怎么不破环单例
这是最易误解的一点:没代理时,customerService() 里调用 userService() 就是普通 Java 方法调用,必然 new 两次;有 CGLIB 代理后,哪怕在同一个类里调用,实际执行的是代理类中重写的 userService(),会先查容器。
- 必须确保调用的是代理对象上的方法,而不是 this.userService() —— 后者绕过代理,直接执行原方法体
- 所以
@Configuration类里不要用this.xxx()调用其他@Bean方法 - 如果真要复用逻辑,建议提取成 private 工具方法,再由各
@Bean方法分别调用
不加 @Configuration 时的典型错误现象
常见表现是日志里反复打印构造函数内容,比如 new UserService() 出现多次,或数据库连接池被初始化多份。更隐蔽的问题是:两个本该共享状态的 Bean 实际指向不同实例,导致事务、缓存、线程安全全部失效。
- Spring Boot 2.6+ 默认开启
spring.aop.proxy-target-class=true,CGLIB 代理是默认行为 - 若配置了
@EnableAspectJAutoProxy(proxyTargetClass = false),可能退化为 JDK Proxy,但@Configuration强制使用 CGLIB,不受此影响 - 自定义
BeanFactoryPostProcessor或提前手动 new 配置类,都会跳过代理机制
@Bean 方法),单例语义就立刻失效。

















