@lazy注解核心是将单例bean创建从容器启动时延迟至首次使用时,通过objectfactory包装实现一次创建、多次复用,适用于报表服务、消息监听器等高开销组件,对prototype bean无效。

Spring 用 @Lazy 延迟加载,核心是把 Bean 的创建从“启动时”挪到“第一次用时”,跳过不必要的初始化,直接缩短启动耗时、降低内存压力。它不改变功能逻辑,只调整时机——适合那些启动不急需、但初始化开销大的组件,比如报表服务、消息监听器、外部客户端等。
@Lazy 可以加在哪儿?三类常见位置
注解作用范围明确,不同位置控制粒度不同:
-
类上(@Component 及其派生):整个组件延迟初始化。例如:
@Service @Lazy
public class ReportGenerator { ... }
该 Service 不会在容器启动时构造,直到被注入或显式 getBean 才触发实例化。 -
@Bean 方法上:仅对该方法返回的 Bean 生效。例如:
@Bean @Lazy
public RedisConnectionFactory redisConn() { return new JedisConnectionFactory(); }
特别适合重量级中间件连接池,避免启动就建连、验活、预热。 -
注入点(@Autowired 字段/参数):不延迟目标 Bean 自身,而是让依赖它的 Bean 拿到一个代理对象,真正调用方法时才初始化目标。例如:
@Autowired @Lazy
private HeavyService heavyService;
这样即使当前类早早创建,heavyService 也不会立刻初始化,直到第一次调用 heavyService.doSomething()。
单例 Bean 延迟后怎么保证只创建一次?
延迟加载不等于每次都新建。@Lazy 对 singleton Bean 的效果是:首次使用时创建并放入单例池,后续所有请求都复用同一个实例。Spring 内部通过 ObjectFactory 包装创建逻辑,在 getBean 或代理方法调用时执行一次 createBean,并将结果注册进 singletonObjects 缓存。所以它仍是标准单例,只是“懒一点”。
注意:@Lazy 对 prototype Bean 无效,因为原型本来就是每次 getBean 都新建,不存在“启动预创建”一说。
全局开关和条件化延迟
如果项目中多数 Bean 都适合延迟,可启用包扫描级默认延迟:
- 在配置类上加:
@ComponentScan(basePackages = "com.example", lazyInit = true) - XML 中设默认行为:
- 需要更精细控制?@Lazy 支持 SpEL 条件表达式:
@Lazy("#{systemProperties['env'] == 'prod'}")
表示仅生产环境才延迟,开发环境仍立即加载便于调试。
验证是否生效的小技巧
别只看代码有没有加注解,要确认行为真的变了:
- 在 Bean 构造函数或 @PostConstruct 方法里加日志,启动后观察输出时机;
- 启动应用后,先不触发任何业务请求,用 Actuator 的 /actuator/beans 端点查目标 Bean 状态,看是否已实例化;
- 配合 JVM 启动参数 -Dspring.trace=true 或开启 DEBUG 日志(org.springframework.beans.factory.support),搜索 “Creating shared instance” 或 “Eagerly caching bean” 等关键词。











