Spring @Lazy注解的定义及详细源码展示
作者:csdn_tom_168
@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 会:
- 不立即创建实例:容器启动时不初始化该 Bean,仅记录其延迟初始化的配置。
- 首次使用时创建:当第一次通过
@Autowired、getBean()等方式获取该 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.properties 中 feature.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 注解,获取其 value 和 condition 属性。
(2)生成代理对象
若 @Lazy 启用(value=true 且 condition 满足),LazyAnnotationBeanPostProcessor 会为该 Bean 生成一个延迟初始化代理(通常是 CGLIB 或 JDK 动态代理)。该代理在首次调用方法时,才会触发实际的 Bean 实例化。
(3)条件判断
若 @Lazy 的 condition 属性存在,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,显著提升应用性能。其核心机制依赖 LazyAnnotationBeanPostProcessor 和 BeanDefinition 的 lazyInit 属性,支持条件化延迟和与多种注解的协同。理解其源码和使用场景,有助于开发者编写更高效、可维护的 Spring 应用。
到此这篇关于Spring @Lazy 详解及详细源码展示的文章就介绍到这了,更多相关Spring @Lazy内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!
