惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The GitHub Blog
The GitHub Blog
云风的 BLOG
云风的 BLOG
T
Threatpost
WordPress大学
WordPress大学
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
PCI Perspectives
PCI Perspectives
T
The Exploit Database - CXSecurity.com
Y
Y Combinator Blog
雷峰网
雷峰网
爱范儿
爱范儿
The Hacker News
The Hacker News
Last Week in AI
Last Week in AI
Simon Willison's Weblog
Simon Willison's Weblog
T
Tor Project blog
S
Securelist
宝玉的分享
宝玉的分享
L
LangChain Blog
O
OpenAI News
AI
AI
P
Privacy International News Feed
L
LINUX DO - 最新话题
D
DataBreaches.Net
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
Attack and Defense Labs
Attack and Defense Labs
罗磊的独立博客
M
MIT News - Artificial intelligence
Security Archives - TechRepublic
Security Archives - TechRepublic
月光博客
月光博客
博客园 - 【当耐特】
T
Tailwind CSS Blog
C
Cybersecurity and Infrastructure Security Agency CISA
H
Help Net Security
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
博客园_首页
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
Hacker News - Newest:
Hacker News - Newest: "LLM"
腾讯CDC
Jina AI
Jina AI
The Last Watchdog
The Last Watchdog
K
Kaspersky official blog
Webroot Blog
Webroot Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Blog — PlanetScale
Blog — PlanetScale
MyScale Blog
MyScale Blog
MongoDB | Blog
MongoDB | Blog
P
Proofpoint News Feed
Recorded Future
Recorded Future
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
博客园 - 三生石上(FineUI控件)
The Cloudflare Blog

博客园 - wastonl

Spring ResolvableType说明 Jvm内存以及垃圾回收相关知识 RocketMQ如何保证消息可靠性 RocketMQ整体架构 mybatis-plus易忘点笔记 SpringMVC使用Resource实现二进制传输(下载) Spring事件异步执行设计与实现 Spring Bean销毁机制 Spring Lifecycle组件 Zipkin Brave使用 Spring Boot日志系统简要介绍 spring cloud sleuth基本使用 tomcat自动刷新响应输出流缓冲区 https碎碎念 ES脚本使用 SpringMVC静态资源处理 Maven插件运行方式 如何使用Maven将项目中的依赖打进jar包 时区以及时区对于Java时间类格式化的影响 SpringMVC处理请求头、响应头、编码行为 tomcat连接处理机制和线程模型 树组件实现 JWT示例与原理 方法句柄API使用
Spring懒加载与@Lazy注解
wastonl · 2025-12-12 · via 博客园 - wastonl

ApplicationContext会在refresh方法中自动初始化所有的单例bean,但是有时候某些bean可能初始比较耗时又或者某种原因想要一个bean在启动时不初始化,而是等到真正使用这个bean时才完成初始化,那么就可以用到这个机制。

在Spring中可以使用@Lazy注解来达到这个目的。

@Lazy注解可以用在类、方法、构造方法、字段、方法参数上。主要是两种使用方法

其一:用于bean的定义,也就是标注在类上或者@Bean标注的方法上,Spring在初始化容器时会自动跳过被@Lazy标注的Bean

@Lazy
@Component
public class LazyBean {
    
}

@Configuration(proxyBeanMethods = false)
public class Config {
    
    @Lazy
    @Bean
    public LazyBean lazyBean() {
        return new LazyBean();
    }
}

初始化所有单例Bean的方法如下

DefaultListableBeanFactory.java

public void preInstantiateSingletons() throws BeansException {
    if (logger.isTraceEnabled()) {
        logger.trace("Pre-instantiating singletons in " + this);
    }

    // Iterate over a copy to allow for init methods which in turn register new bean definitions.
    // While this may not be part of the regular factory bootstrap, it does otherwise work fine.
    List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

    // Trigger initialization of all non-lazy singleton beans...
    for (String beanName : beanNames) {
        RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
        // 单例并且非懒加载
        if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
            if (isFactoryBean(beanName)) {
                Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
                if (bean instanceof SmartFactoryBean<?> smartFactoryBean && smartFactoryBean.isEagerInit()) {
                    getBean(beanName);
                }
            }
            else {
                getBean(beanName);
            }
        }
    }
    省略下面代码
}

其二:用于字段注入(包括字段注入,构造方法注入,setter方法注入,以及@Bean方法的参数上),Spring在注入字段时,如果发现被注入的字段有被@Lazy注解标记,则会注入一个代理对象。这里的判断不会管注入的Bean本身是否有@Lazy标记

@Component
public class CommonBean {
    
    /**
     * 无论LazyBean本身定义是是否有被@Lazy标记,都是注入的代理对象
     */
    @Lazy
    @Autowired
    private LazyBean lazyBean;
}

最后注意的点

一个标记为Lazy的bean如果没有被其他bean依赖,那么只会在真正使用时才会进行初始化,也就是调用它的方法(toString也会,getClass方法不会,因为没有代理getClass方法)。如果有被其它bean依赖注入,除非注入时也标记了@Lazy,否则也会简介导致这个Lazy bean进行初始化。

如果手动使用容器的getBean方法去获取这个Lazy Bean,也会导致其初始化,因为getBean方法本身没有是否懒加载的判断。

依赖注入时注入的代理对象不会注册到容器中,当调用目标对象方法时,底层会调用getBean方法获取到目标对象,此时把目标对象注册到容器中,当然注册是getBean方法内部行为。

看以下示例代码

@Lazy
@Component
public class LazyBean {

    public LazyBean() {
        System.out.println("=======lazyBean init=======");
    }
}

public class CommonBean {

    private final LazyBean lazyBean;
	
    // 这里没有使用@Lazy来标注
    public CommonBean(LazyBean lazyBean) {
        this.lazyBean = lazyBean;
        System.out.println("======commonBean init=======");
    }

    public LazyBean getLazyBean() {
        return lazyBean;
    }
}

// 测试代码
@Test
public void lazyTest() {
    System.out.println("=========lazy test=========");
    System.out.println(commonBean.getLazyBean().getClass());

    Map<String, LazyBean> lazyBeanMap = applicationContext.getBeansOfType(LazyBean.class);
    lazyBeanMap.forEach((k, v) -> {
        System.out.println("beanName: " + k + ", value: " + v.getClass());
    });
}
	

输出结果:

=lazyBean init=

commonBean init=

=lazy test=

class com.wangtao.springboot3.lazy.LazyBean

beanName: lazyBean, value: class com.wangtao.springboot3.lazy.LazyBean

可以看到即便LazyBean已经被@Lazy标注,但是由于被CommonBean依赖注入,而导致直接初始化了。

再看使用@Lazy注解来注入

@Component
public class CommonBean {

    private final LazyBean lazyBean;

    // 仅仅在这里加了注解
    public CommonBean(@Lazy LazyBean lazyBean) {
        this.lazyBean = lazyBean;
        System.out.println("======commonBean init=======");
    }

    public LazyBean getLazyBean() {
        return lazyBean;
    }
}

输出结果

commonBean init=

=lazy test=

class com.wangtao.springboot3.lazy.LazyBean$$SpringCGLIB$$0

=lazyBean init=

beanName: lazyBean, value: class com.wangtao.springboot3.lazy.LazyBean

可以看到在手动调用getBeansOfType方法后才执行了初始化