final修饰符与@Lazy注解是否存在冲突?
关于Spring @Lazy与构造注入的懒加载问题
你的假设在未使用参数级@Lazy的场景下完全成立,但Spring其实提供了专门的机制,让构造注入(哪怕是final字段)也能实现依赖的懒加载,这是容易被忽略的细节:
核心逻辑:构造注入的默认行为
构造函数调用的本质是必须传入所有参数值,所以默认情况下,不管你给当前Bean加不加@Lazy,只要构造参数是另一个Bean,Spring都会立即初始化这个依赖Bean来完成构造——毕竟你总不能给构造函数传个"空"来初始化final字段,也没法在构造完成后再反射修改final字段(这会破坏Java的语义)。
打破默认:参数级@Lazy+动态代理
Spring的@Lazy注解可以直接标记在构造函数的参数上,这时候Spring不会直接初始化依赖的真实Bean,而是生成一个动态代理对象传入构造函数。这个代理会在你第一次实际调用依赖的方法/访问属性时,才触发真实Bean的初始化。
举个实际代码例子:
@Component public class MyService { // final字段完全不影响 private final LazyDao lazyDao; // 给构造参数添加@Lazy public MyService(@Lazy LazyDao lazyDao) { this.lazyDao = lazyDao; // 此时lazyDao是代理对象,LazyDao的构造方法还没执行 System.out.println("MyService initialized"); } public void queryData() { // 第一次调用lazyDao的方法时,才会初始化真实的LazyDao实例 lazyDao.fetch(); } } @Component public class LazyDao { public LazyDao() { System.out.println("LazyDao initialized"); } public void fetch() {} }
启动应用时只会打印MyService initialized,直到调用queryData()方法,才会打印LazyDao initialized。
关于无final字段的情况
如果字段没有final修饰,但没给构造参数加@Lazy,构造调用时依然会立即初始化依赖Bean——因为构造函数必须拿到参数值才能创建实例,Spring不会在构造完成后再反射注入这个字段(这不是构造注入的语义)。只有用参数级@Lazy,才能实现构造注入场景下的依赖懒加载,和字段是否final无关。
总结
- 你的原始假设(构造注入默认无法懒加载依赖,不管字段是否final)是对的,但这是未使用参数级@Lazy的情况;
- Spring通过参数级@Lazy+动态代理的机制,让构造注入(包括final字段)也能实现依赖的懒加载,这是关键的解决方案。
内容的提问来源于stack exchange,提问作者Poltix
相关产品推荐
相关产品推荐

