Spring系列——@lazy注解

本文翻译自相关文章,介绍Spring的@Lazy注解。默认Spring在应用启动时创建单例bean,而使用@Lazy注解可实现懒加载,即请求时再创建bean。文中讨论了两种实现懒加载实例化bean的方法,一是在@Configuration class中使用,二是使用@Autowired时使用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

翻译自https://www.baeldung.com/spring-lazy-annotation

1.概述

默认情况下,Spring会在应用程序上下文的启动时创建所有单例bean。这背后的原因很简单:立即避免和检测所有可能的错误,而不是在运行时。
但是,有些情况下我们需要创建一个bean,而不是在应用程序上下文启动时,而是在我们请求时。
在这个快速教程中,我们将讨论Spring的@Lazy注释。

2.懒加载

这个注解出现在Spring 3.0以后,有好几种方法实现来懒加载实例化bean.

2.1 @Configuration class

@Configuraiton@lazy 一起使用时,意味着所有使用@Bean 的方法都是懒加载

@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

这里,为了初始化一个懒惰的bean,我们从另一个bean中引用它。

我们想要懒惰加载的bean:

@Lazy
@Component
public class City {
    public City() {
        System.out.println("City bean initialized");
    }
}
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() {
    // load up ctx appication context
    Region region = ctx.getBean(Region.class);
    region.getCityInstance();
}

在这里,当我们调用getCityInstance(0 方法时,city bean 才被初始化。

### Spring框架中通过@Lazy注解解决循环依赖问题 在Spring框架中,`@Lazy`注解提供了一种机制来解决某些类型的循环依赖问题。当两个或多个bean之间存在相互依赖关系时,可能会导致容器初始化失败。然而,在特定情况下,可以通过`@Lazy`注解延迟加载bean的方式绕过这种问题。 #### 循环依赖的背景 Spring中的循环依赖通常发生在以下两种场景之一: 1. 构造器注入方式下的单例模式bean之间的循环依赖。 2. Setter方法或其他自定义注入方式下的循环依赖。 对于第一种情况,Spring默认支持基于构造器注入的单例bean间的循环依赖解决方案;而对于第二种情况,则可能需要借助于`@Lazy`注解来解决问题[^2]。 #### `@Lazy`的工作原理 `@Lazy`注解的核心作用在于延迟bean的实例化过程。具体来说,它会在实际需要使用某个bean的时候才去创建其实例,而不是在应用上下文启动阶段就立即完成实例化操作。以下是其主要应用场景: - **标记在普通bean类上** 被标注为`@Lazy`的bean会被推迟至第一次访问时才会触发实例化流程。 - **标记在@Configuration配置类上** 如果在一个带有`@Configuration`注解的类上加上`@Lazy`,则整个配置类以及其中声明的所有`@Bean`都会受到懒加载的影响。 - **用于@Autowired字段或者参数处** 当`@Lazy`应用于`@Autowired`修饰的成员变量或方法参数时,表示这些地方所引用的对象也应采用懒加载策略。需要注意的是,这仅影响注入时机而不会改变目标bean本身的生命周期特性。 #### 解决循环依赖的具体实现细节 为了理解`@Lazy`是如何帮助缓解循环依赖难题的,我们需要深入探讨一下Spring内部处理依赖解析的过程。假设A和B构成了典型的双向依赖结构——即A持有对B的引用的同时,B又反过来指向了A。如果两者都未设置任何特殊的加载选项,默认情况下就会引发异常因为无法决定谁先被完全构建出来供另一方消费。 但是假如我们给其中一个方向加上了`@Lazy`标签呢?比如让B成为懒载入型组件的话,那么事情便有了转机:虽然此时仍然要经历一轮初步装配动作(也就是把尚未成熟的半成品版本交给对方),但由于涉及到了后者部分的关键环节已被延缓执行的缘故,所以整体链条得以顺利贯通下去直到最终形成稳定状态为止[^3]。 下面是简化版伪代码展示这一交互过程的一部分片段: ```java @Override @Nullable public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) { return isLazy(descriptor) ? buildLazyResolutionProxy(descriptor, beanName) : null; } ``` 上述方法来自DefaultListableBeanFactory类内部定义,负责判断当前描述符是否满足懒加载条件并据此返回相应的代理对象而非真实主体本身。这样做的好处就在于能够暂时规避掉那些可能导致死锁风险的操作序列从而保障系统的正常运转。 另外值得一提的小插曲就是关于代理生成工具的选择方面。在这里提到过的ProxyFactory实际上正是承担此项重任的主要角色之一,并且由于是非线程安全的设计因而建议避免共享同一个实例跨多线程环境重复利用的情况发生[^5]。 最后再补充一句有关自动装配处理器注册时间点的信息。我们知道像AutowiredAnnotationBeanPostProcessor这样的特殊组件往往早早就已经加入到全局管理池当中等待后续发挥作用的机会到来。它们的存在意义就在于能够在恰当的时间介入业务逻辑之中按照预定规则完成必要的绑定任务等等[^4]。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值