Spring @Lazy注解的定义及详细源码展示

 更新时间:2025年09月02日 11:44:04   作者:csdn_tom_168  
@Lazy是Spring延迟初始化Bean的注解,通过推迟实例化时机减少启动时间与内存占用,适用于高成本Bean、条件化延迟及解决循环依赖,但需注意首次使用性能开销与依赖顺序问题,本文给大家介绍Spring @Lazy注解的定义及详细源码展示,感兴趣的朋友一起看看吧

@Lazy 是 Spring 框架中用于延迟初始化 Bean的核心注解,其核心作用是将 Bean 的实例化时机从“容器启动时”推迟到“首次被使用时”,从而减少应用启动时间、降低内存占用,尤其适用于初始化成本高或非立即使用的 Bean。以下从注解定义、源码解析、核心功能、使用场景注意事项展开详细说明。

一、@Lazy注解的定义与源码解析

@Lazy 位于 org.springframework.context.annotation 包中,其源码定义如下(简化版):

@Target({ElementType.TYPE, ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Lazy {
    /**
     * 是否延迟初始化(默认 true,即延迟;false 表示立即初始化)
     */
    boolean value() default true;
    /**
     * 条件判断(可选,满足条件时才延迟初始化)
     */
    String condition() default "";
}

关键属性说明

  • value:布尔值,控制是否启用延迟初始化(默认 true)。
  • condition:SpEL 表达式(可选),仅当表达式结果为 true 时,才启用延迟初始化(用于条件化延迟)。

二、核心功能:延迟初始化的实现机制

1.默认行为:立即初始化

Spring 容器默认在启动时初始化所有单例 Bean(singleton 作用域),无论是否被使用。这可能导致:

  • 启动时间过长(尤其是初始化成本高的 Bean)。
  • 内存浪费(未使用的 Bean 占用资源)。

2.@Lazy的延迟初始化逻辑

当 Bean 被 @Lazy 标记后,Spring 会:

  1. 不立即创建实例:容器启动时不初始化该 Bean,仅记录其延迟初始化的配置。
  2. 首次使用时创建:当第一次通过 @AutowiredgetBean() 等方式获取该 Bean 时,才触发实例化。

3.与作用域的协同

@Lazy 对不同作用域的 Bean 影响不同:

作用域默认初始化时机@Lazy 效果
singleton(单例)容器启动时初始化延迟到首次使用时初始化(全局仅一次)。
prototype(原型)每次 getBean() 时初始化无变化(原型 Bean 本就每次新建,@Lazy 不影响)。
request/session(Web 作用域)作用域创建时初始化延迟到作用域首次使用时初始化(如 HTTP 请求首次访问时)。

三、典型使用场景与示例

1.高初始化成本的 Bean

例如,数据库连接池(如 HikariCP)、缓存客户端(如 RedisTemplate)等,初始化时需要加载大量配置或建立长连接,延迟初始化可显著减少启动时间。

示例

@Service
@Lazy // 延迟初始化(首次使用时创建)
public class RedisClient {
    private final RedisTemplate<String, Object> redisTemplate;
    // 构造器初始化成本高(连接 Redis 服务器)
    public RedisClient(RedisConnectionFactory connectionFactory) {
        this.redisTemplate = new RedisTemplate<>();
        this.redisTemplate.setConnectionFactory(connectionFactory);
        this.redisTemplate.afterPropertiesSet(); // 触发连接
    }
    public void setValue(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }
}

效果:应用启动时,RedisClient 不会立即连接 Redis,而是在第一次调用 setValue 时才初始化。

2.条件化延迟加载

结合 condition 属性,仅当满足特定条件时才延迟初始化(如根据环境变量或配置属性)。

示例

@Service
@Lazy(condition = "#{environment.getProperty('feature.analytics.enabled') == 'true'}") // 仅当 analytics 功能启用时延迟初始化
public class AnalyticsService {
    // 初始化成本高(连接分析服务)
    public AnalyticsService(AnalyticsClient client) {
        // ...
    }
}

效果:若 application.propertiesfeature.analytics.enabled=false,则 AnalyticsService 会被立即初始化(即使未使用);若为 true,则延迟到首次使用时初始化。

3.避免循环依赖

在循环依赖场景中,@Lazy 可打破“构造器注入导致的循环依赖死锁”。例如,A 依赖 B,B 依赖 A,若两者均使用构造器注入,Spring 无法解决循环依赖;但通过 @Lazy 延迟其中一个的初始化,可避免启动时报错。

示例

@Service
public class A {
    private final B b;
    // 构造器注入 B(但 B 被 @Lazy 标记)
    @Autowired
    public A(@Lazy B b) {
        this.b = b;
    }
}
@Service
@Lazy // B 延迟初始化
public class B {
    private final A a;
    // 构造器注入 A(A 已初始化,无循环依赖)
    @Autowired
    public B(A a) {
        this.a = a;
    }
}

效果:A 初始化时,B 被标记为延迟,因此不会立即触发 B 的构造器(避免循环依赖)。当首次使用 B 时,A 已存在,可正常初始化。

四、源码实现细节与关键类

1.LazyAnnotationBeanPostProcessor

Spring 处理 @Lazy 的核心类,继承自 InstantiationAwareBeanPostProcessorAdapter,负责将 @Lazy 标记的 Bean 转换为“延迟初始化”的代理对象。其关键逻辑如下:

(1)扫描@Lazy注解

在解析 @Bean 方法或 @Component 类时,ConfigurationClassParser 会扫描 @Lazy 注解,获取其 valuecondition 属性。

(2)生成代理对象

@Lazy 启用(value=truecondition 满足),LazyAnnotationBeanPostProcessor 会为该 Bean 生成一个延迟初始化代理(通常是 CGLIB 或 JDK 动态代理)。该代理在首次调用方法时,才会触发实际的 Bean 实例化。

(3)条件判断

@Lazycondition 属性存在,LazyAnnotationBeanPostProcessor 会通过 ExpressionEvaluator 解析 SpEL 表达式,判断是否启用延迟初始化。

2.BeanDefinition的lazyInit属性

Spring 的 BeanDefinition 接口有一个 isLazyInit() 方法,用于标记该 Bean 是否延迟初始化。LazyAnnotationBeanPostProcessor 会根据 @Lazy 的配置设置该属性为 true,从而告知 Spring 容器延迟初始化该 Bean。

五、注意事项与常见问题

1.首次使用的性能开销

延迟初始化的 Bean 在首次使用时会触发实例化,可能导致短暂的延迟(如连接数据库、加载配置)。需权衡启动时间与首次使用时间的总和。

2.与@PostConstruct的顺序

若 Bean 同时被 @Lazy@PostConstruct 标记,@PostConstruct 方法会在 Bean 实例化后(首次使用时)执行,而非容器启动时。

3.循环依赖的限制

@Lazy 可打破构造器注入的循环依赖,但无法解决所有循环依赖场景(如字段注入的循环依赖仍需通过 @Lazy 或重构代码解决)。

4.与@Conditional的协同

@Lazy 可与 @Conditional 注解(如 @ConditionalOnProperty)结合使用,实现更灵活的条件化延迟初始化。例如:仅当某个属性为 true 时,才延迟初始化。

5.原型作用域的@Lazy

prototype 作用域的 Bean 本身每次 getBean() 时都会创建新实例,@Lazy 对其无影响(因为 prototype 本就延迟初始化)。

6.全局懒加载配置

Spring Boot 支持全局懒加载(通过 spring.main.lazy-initialization=true),此时所有单例 Bean 默认延迟初始化(除非显式标记 @Lazy(false))。@Lazy 可覆盖全局配置。

六、总结

@Lazy 是 Spring 中优化启动时间和资源占用的核心注解,通过延迟初始化非立即使用的 Bean,显著提升应用性能。其核心机制依赖 LazyAnnotationBeanPostProcessorBeanDefinitionlazyInit 属性,支持条件化延迟和与多种注解的协同。理解其源码和使用场景,有助于开发者编写更高效、可维护的 Spring 应用。

到此这篇关于Spring @Lazy 详解及详细源码展示的文章就介绍到这了,更多相关Spring @Lazy内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Spring Security登录表单配置示例详解

    Spring Security登录表单配置示例详解

    这篇文章主要介绍了Spring Security登录表单配置,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • 使用Filter实现登录权限验证

    使用Filter实现登录权限验证

    这篇文章主要为大家详细介绍了使用Filter实现登录权限验证,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-10-10
  • JavaWeb开发之使用jQuery与Ajax实现动态联级菜单效果

    JavaWeb开发之使用jQuery与Ajax实现动态联级菜单效果

    这篇文章主要介绍了JavaWeb开发之使用jQuery与Ajax实现动态联级菜单效果的相关资料,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2016-10-10
  • JAVA字符串拼接常见方法汇总

    JAVA字符串拼接常见方法汇总

    这篇文章主要介绍了JAVA字符串拼接常见方法,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-06-06
  • java 实现切割文件和合并文件的功能

    java 实现切割文件和合并文件的功能

    这篇文章主要介绍了java 实现切割文件和合并文件的功能的相关资料,这里实现文件的切割的实现代码和文件合并的实现代码,需要的朋友可以参考下
    2017-07-07
  • springboot 多环境切换的方法

    springboot 多环境切换的方法

    这篇文章主要介绍了springboot 多环境切换的方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • @SpringBootTest 注解报红问题及解决

    @SpringBootTest 注解报红问题及解决

    这篇文章主要介绍了@SpringBootTest 注解报红问题及解决,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • java实现学生信息录入界面

    java实现学生信息录入界面

    这篇文章主要为大家详细介绍了java实现学生信息录入界面,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-04-04
  • 新手了解java 泛型基础知识

    新手了解java 泛型基础知识

    这篇文章主要给大家介绍了关于Java中泛型使用的简单方法,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2021-07-07
  • Mybatis中如何设置sqlSession自动提交

    Mybatis中如何设置sqlSession自动提交

    在MyBatis中,默认情况下,获取的SqlSession对象不会自动提交事务,这意味着在进行更新、删除或插入等操作后,需要显式调用commit方法来提交事务,但是,可以在获取SqlSession时通过将openSession方法的参数设置为true
    2024-09-09

最新评论