1. 概述

默认情况下,Spring 会在应用上下文启动时立即创建所有单例 bean。 这么做的原因很简单:在启动时就发现并解决所有潜在问题,而不是等到运行时才报错。

但有些场景下,我们希望 bean 不是在应用上下文启动时创建,而是在首次请求时才创建。

这篇快速指南将深入探讨 Spring 的 @Lazy 注解。

2. 延迟初始化

@Lazy 注解自 Spring 3.0 版本就已存在。有几种方式可以告诉 IoC 容器延迟初始化 bean。

2.1. 在 @Configuration 类上使用

当我们在 @Configuration 类上添加 @Lazy 注解时,表示该类中所有带 @Bean 注解的方法都应该延迟加载。

这等价于 XML 配置中的 default-lazy-init="true" 属性。

看个例子:

@Lazy
@Configuration
@ComponentScan(basePackages = "com.baeldung.lazy")
public class AppConfig {

    @Bean
    public Region getRegion(){
        return new Region();
    }

    @Bean
    public Country getCountry(){
        return new Country();
    }
}

测试一下效果:

@Test
public void givenLazyAnnotation_whenConfigClass_thenLazyAll() {

    AnnotationConfigApplicationContext ctx
     = new AnnotationConfigApplicationContext();
    ctx.register(AppConfig.class);
    ctx.refresh();
    ctx.getBean(Region.class);
    ctx.getBean(Country.class);
}

可以看到,所有 bean 都在首次请求时才创建:

Bean factory for ...AnnotationConfigApplicationContext: 
...DefaultListableBeanFactory: [...];
// application context started
Region bean initialized
Country bean initialized

如果只想对特定 bean 应用延迟加载,移除类上的 @Lazy,然后在目标 bean 的配置上添加:

@Bean
@Lazy(true)
public Region getRegion(){
    return new Region();
}

2.2. 配合 @Autowired 使用

继续前,建议先了解 @Autowired@Component 注解的基础知识。

这里的关键是通过另一个 bean 来引用延迟初始化的 bean。

要延迟加载的 bean:

@Lazy
@Component
public class City {
    public City() {
        System.out.println("City bean initialized");
    }
}

引用它的 bean:

public class Region {

    @Lazy
    @Autowired
    private City city;

    public Region() {
        System.out.println("Region bean initialized");
    }

    public City getCityInstance() {
        return city;
    }
}

注意:两处都需要添加 @Lazy 注解。

City 类上使用 @Component 注解,并通过 @Autowired 引用:

@Test
public void givenLazyAnnotation_whenAutowire_thenLazyBean() {
    // 加载应用上下文
    Region region = ctx.getBean(Region.class);
    region.getCityInstance();
}

这里 City bean 只有在调用 getCityInstance() 方法时才会初始化。

3. 总结

这篇快速指南中,我们学习了 Spring @Lazy 注解的基础知识,并探讨了多种配置和使用方式。

✅ 关键要点:

  • 延迟初始化能优化启动速度,但可能掩盖配置问题
  • 类级别注解影响所有 bean,方法级别只针对特定 bean
  • 配合 @Autowired 使用时,两处都需要 @Lazy

⚠️ 踩坑提醒:

  • 延迟加载的 bean 如果存在循环依赖,会在首次使用时才暴露问题
  • 启动时无法发现的错误会推迟到运行时

完整代码示例可在 GitHub 获取。