Spring框架懒加载怎么实现?

Spring 懒加载实现全解析:让你的应用启动快人一步

在实际项目开发中,我们可能遇到这样的场景:应用启动时,Spring 容器会迫不及待地创建所有单例 Bean,即使有些 Bean 在很长一段时间内都用不到。这不仅拖慢了启动速度,还占用了宝贵的内存。Spring 的"懒加载"(Lazy Initialization)正是为了解决这个问题而生。今天这篇博客,带你从概念到实现,再到原理,彻底玩转 Spring 懒加载。


一、什么是懒加载?

懒加载 是一种设计思想:等到真正需要某个资源的时候,才去创建或加载它 。

在 Spring 容器中,默认的行为是 迫切加载 (Eager Initialization)------ 所有单例 Bean 会在容器启动阶段完成实例化与依赖注入。而开启懒加载后,单例 Bean 的创建时机被推迟到第一次被请求时(如 getBean() 或依赖注入到另一个 Bean)。

下面这张图直观对比了两种策略的区别:

二、如何开启 Spring 懒加载?

Spring 提供了非常灵活的配置方式,可以用注解,也可以用 XML,还能做全局开关。

1. 使用 @Lazy 注解

最直接的方式,在需要懒加载的 Bean 上标注 @Lazy。

java 复制代码
@Component
@Lazy
public class EmailService {
    public EmailService() {
        System.out.println("EmailService 被创建了!");
    }

    public void send(String message) {
        System.out.println("发送邮件:" + message);
    }
}

在配置类里搭配 @Bean 使用:

java 复制代码
@Configuration
public class AppConfig {
    @Bean
    @Lazy
    public EmailService emailService() {
        return new EmailService();
    }
}

此时,Spring 容器启动时并不会创建 EmailService,直到有人调用 context.getBean(EmailService.class) 或它被注入到其他 Bean 并被使用时才会实例化。

懒加载也能用在注入点 :如果某个 Bean 本身不是懒加载,但想让它依赖的 Bean 延迟初始化,可以在注入点加 @Lazy。

java 复制代码
@Component
public class NotificationManager {
    @Autowired
    @Lazy
    private EmailService emailService;

    public void notifyUser() {
        emailService.send("欢迎注册"); // 此时才创建 EmailService
    }
}

2. 通过 XML 配置懒加载

在传统 XML 配置中,可以给 <bean> 标签添加 lazy-init="true" 属性。

xml 复制代码
<bean id="emailService" class="com.example.EmailService" lazy-init="true" />

如果想全局默认懒加载,在 <beans> 根元素加上 default-lazy-init="true":

xml 复制代码
<beans default-lazy-init="true">
    <bean id="emailService" class="com.example.EmailService" />
    <bean id="smsService" class="com.example.SmsService" />
</beans>

但要注意,如果某个 Bean 显式设置了 lazy-init="false",则以自身设置为准。

3. Spring Boot 全局开关

在 Spring Boot 应用中,可以在 application.properties 里添加一行配置:

properties 复制代码
spring.main.lazy-initialization=true

这会让所有 Bean 都默认懒加载。如果有些 Bean 必须立即初始化,可以单独加上 @Lazy(false) 覆盖全局设置。


三、懒加载的内部原理

知其然,也要知其所以然。Spring 是如何实现懒加载的?核心围绕 BeanFactory 的创建流程。

Spring 在读取 BeanDefinition 时,会检查 lazyInit 属性。若为 true,则在容器刷新阶段的 finishBeanFactoryInitialization() 中跳过该 Bean 的实例化。真正触发创建的地方,是 AbstractBeanFactory 的 getBean() 方法,其中调用了 getSingleton(),若缓存中不存在,则会走 createBean() 流程,并最终放入单例缓存。

小陷阱:懒 Bean 被非懒 Bean 依赖

如果一个懒加载的 Bean 被另一个非懒加载的 Bean 注入了,那么懒加载会失效,因为它会在容器启动时作为依赖被提前实例化。这是 Spring 依赖解析的必然结果,使用时务必注意。


四、懒加载实战演示

假设我们有如下三个组件:

  • OrderService:急切加载
  • EmailService:懒加载
  • SmsService:急切加载,但依赖 EmailService
java 复制代码
@Component
public class OrderService {
    public OrderService() {
        System.out.println("OrderService 实例化");
    }
}

@Component
@Lazy
public class EmailService {
    public EmailService() {
        System.out.println("EmailService 实例化");
    }
}

@Component
public class SmsService {
    @Autowired
    private EmailService emailService;

    public SmsService() {
        System.out.println("SmsService 实例化");
    }
}

启动容器,你会看到控制台输出:

复制代码
OrderService 实例化
EmailService 实例化
SmsService 实例化

咦?EmailService 明明是懒加载,为什么被创建了?因为 SmsService 是迫切加载,它又依赖了 EmailService,为了完成依赖注入,容器只好提前实例化 EmailService。
解决办法 :在 SmsService 的注入点上也加 @Lazy,这样注入的是懒代理,不会立刻触发 EmailService 的实例化。

java 复制代码
@Autowired
@Lazy
private EmailService emailService;

这样再运行,控制台输出:

复制代码
OrderService 实例化
SmsService 实例化

EmailService 没有被创建,直到 SmsService 中真正调用 emailService.send(...) 时才会初始化。


五、何时该用懒加载?优缺点一览

优点

  • 加快启动速度:减少初始化时间,尤其适合微服务、Serverless 等对冷启动敏感的场景。
  • 节省内存:不立即使用的 Bean 不占用资源。
  • 按需加载:大型应用中,部分模块可能从不被访问,懒加载避免无用开销。

缺点

  • 隐藏错误:配置错误或依赖缺失在启动时不会被发现,延迟到运行时才暴露,增加排查难度。
  • 首次调用延迟:第一次请求时会触发 Bean 创建,可能带来较高的响应延迟(冷启动 spike)。
  • 复杂性:需要小心处理依赖关系,避免意外的迫切初始化。

推荐实践

  • 开发环境可全局关闭懒加载,尽早暴露配置问题。
  • 生产环境可根据需要开启全局懒加载,并用 @Lazy(false) 将核心 Bean(如数据库连接池、监控端点)标记为迫切加载。
  • 微服务架构下,Spring Boot 全局懒加载是一个常见优化手段。

六、总结

Spring 的懒加载通过 @Lazy 注解、XML 属性或 Boot 全局配置即可轻松实现。它的核心原理是延迟 Bean 的创建时机,直到第一次被请求。虽然能显著提升启动速度,但也会带来错误延时暴露等问题,因此需要根据实际情况审慎选择。

掌握懒加载,是在大型应用与云原生架构下进行启动优化的必修课。希望这篇博客能帮你透彻理解并灵活运用这一特性。如果觉得有帮助,欢迎点赞收藏,也欢迎在评论区交流你的实践心得!

相关推荐
CCblandy9 小时前
令牌桶在 0.9 倍负载超发 19%、固定窗口边界放行 20 个:四种限流算法同构实测
后端·python·算法·性能优化·架构
Cc.Y10 小时前
Java零基础入门:深入Java核心:文件IO与序列化全解
java·开发语言·python
拿本唠嗑AI研究10 小时前
本体工程实战指南:从建模到可维护的完整落地路径
java·人工智能·python·jena·ontology
Smoothcloud润云10 小时前
PyTorch推理服务模型热切换与失败回滚实战:降低版本更新中断
服务器·人工智能·pytorch·python·深度学习·gpu
jsjzsl210 小时前
递归自改进系统(RSI)的本质是自发对称破缺:智能爆炸有界—— AI研发切记盲目投入
人工智能·python·算法
2601_9548118211 小时前
机器视觉教学套件选型指南:五维技术参数对比与课程体系评估
python·数码相机
小马不起床11 小时前
零基础玩转 FastAPI_05:后端课程模块
python·fastapi
SL_staff11 小时前
为什么业务总在等BI报表?JVS-BI的可视化ELT实践解析
spring·数据分析·数据可视化
小王20412 小时前
Day 41:模型注意力机制 — SE / CBAM / ECA
python·深度学习·机器学习
171320330计算机毕设编程12 小时前
计算机毕业设计 基于YOLO改进算法的车辆特征识别系统设计与实现--lw
java·大数据·python·sql·算法