ZetCode

Spring Boot @Lazy

上次修改时间:2023 年 7 月 23 日

在本文中,我们将展示如何使用 Spring 的 @Lazy 注解进行懒加载 Bean 初始化。

Spring 是一个流行的 Java 应用程序框架,用于创建企业应用程序。Spring Boot 是 Spring 框架的演进,它有助于以最小的 effort 创建基于 Spring 的独立、生产级应用程序。

@Lazy

@Lazy 注解指示是否懒加载一个 Bean。它可以在 @Component@Bean 定义中使用。一个 @Lazy Bean 在被另一个 Bean 引用或从 BeanFactory 显式检索之前不会被初始化。未用 @Lazy 注解的 Bean 将被急切地初始化。

Spring Boot @Lazy 示例

在下面的示例中,我们创建了懒加载和急切加载的 Bean。它演示了这两种类型的 Bean 之间的区别。该应用程序是一个简单的 Spring Boot Web 应用程序,在嵌入式 Tomcat 服务器上运行。我们使用 Freemarker 模板引擎。

build.gradle
...
src
├── main
│   ├── java
│   │   └── com
│   │       └── zetcode
│   │           ├── Application.java
│   │           ├── bean
│   │           │   ├── MyBean.java
│   │           │   ├── MyLazyBean.java
│   │           │   └── StartUpBean.java
│   │           └── controller
│   │               └── MyController.java
│   └── resources
│       ├── application.properties
│       ├── static
│       │   └── index.html
│       └── templates
│           └── showMessages.ftlh
└── test
    └── java

这是项目结构。

build.gradle
plugins {
    id 'org.springframework.boot' version '3.1.1'
    id 'io.spring.dependency-management' version '1.1.0'
    id 'java'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'
sourceCompatibility = '17'

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-web'
    implementation 'org.springframework.boot:spring-boot-starter-freemarker'
}

这是 build.gradle 文件。

resources/application.properties
spring.main.banner-mode=off
logging.level.org.springframework=ERROR

application.properties 中,我们关闭了启动横幅并设置了日志级别。

com/zetcode/bean/MyBean.java
package com.zetcode.bean;

import org.springframework.stereotype.Component;

import java.util.logging.Logger;

@Component
public class MyBean {

    static Logger log = Logger.getLogger(MyBean.class.getName());

    public MyBean() {

        log.info("MyBean initialized");
    }

    public String getMessage() {

        return "Message from MyBean";
    }
}

这是 MyBean。该 Bean 被急切地初始化,即在 Spring 框架启动时。

com/zetcode/bean/MyLazyBean.java
package com.zetcode.bean;

import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.util.logging.Logger;

@Component
@Lazy
public class MyLazyBean {

    static Logger log = Logger.getLogger(MyLazyBean.class.getName());

    public MyLazyBean() {

        log.info("MyLazyBean initialized");
    }

    public String getMessage() {

        return "Message from MyLazyBean";
    }
}

MyLazyBean 包含 @Lazy 注解。它在第一次被请求时进行懒加载。它从控制器中被请求。

com/zetcode/bean/StartUpBean.java
package com.zetcode.bean;

import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.stereotype.Component;

import java.util.logging.Logger;

@Component
public class StartUpBean implements 
        ApplicationListener<ApplicationReadyEvent> {

    static Logger log = Logger.getLogger(StartUpBean.class.getName());

    @Override
    public void onApplicationEvent(ApplicationReadyEvent applicationReadyEvent) {

        log.info("Application is ready");
    }
}

StartUpBean 实现了一个应用程序监听器;当应用程序准备就绪时,它会记录一条消息。

com/zetcode/controller/MyController.java
package com.zetcode.controller;

import com.zetcode.bean.MyBean;
import com.zetcode.bean.MyLazyBean;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class MyController {
    
    private final BeanFactory factory;

    @Autowired
    public MyController(BeanFactory factory) {
        this.factory = factory;
    }

    @GetMapping(path="/messages")
    public String getMessages(Model model) {

        MyLazyBean myLazyBean = factory.getBean(MyLazyBean.class);
        MyBean myBean = factory.getBean(MyBean.class);

        model.addAttribute("mybean", myBean.getMessage());
        model.addAttribute("mylazybean", myLazyBean.getMessage());

        return "showMessages";
    }
}

这是一个控制器类。它创建了两个 Bean 并接收它们的消息。这些消息显示在 Freemarker 模板中。

private final BeanFactory factory;

@Autowired
public MyController(BeanFactory factory) {
    this.factory = factory;
}

我们注入了 BeanFactory。该工厂用于访问 Spring Bean。

MyLazyBean myLazyBean = factory.getBean(MyLazyBean.class);

这是 MyLazyBean 被初始化的时刻。

MyBean myBean = factory.getBean(MyBean.class);

我们从工厂中获取 MyBeanMyBean 在 Spring 启动时被初始化。

resources/templates/showMessages.ftlh
<!DOCTYPE html>
<html>
    <head>
        <title>Show data</title>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        </head>
    <body>

        <p>
            MyBean: ${mybean}
        </p>

        <p>
            MyLazyBean: ${mylazybean}
        </p>

    </body>
</html>

Freemarker 模板显示来自两个 Bean 的消息。

resources/static/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Home page</title>
</head>
<body>

<a href="messages">Get messages</a>

</body>
</html>

index.html 中有一个链接可以获取来自 Bean 的消息。

com/zetcode/Application.java
package com.zetcode;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application {

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Application 设置了 Spring Boot 应用程序。@SpringBootApplication 启用了自动配置和组件扫描。

$ ./gradlew bootRun

我们启动应用程序。

Initializing Spring embedded WebApplicationContext
com.zetcode.bean.MyBean : MyBean initialized
com.zetcode.Application : Started Application in 2.483 seconds (JVM running for 2.854)
com.zetcode.bean.StartUpBean : Application is ready

当 Spring Boot 启动时,我们可以看到这些日志消息。请注意,MyBean 在启动时被初始化。

com.zetcode.bean.MyLazyBean : MyLazyBean initialized

当控制器被调用时,MyLazyBean 被初始化。

在本文中,我们展示了如何使用 Spring @Lazy 注解。

作者

我的名字是 Jan Bodnar,我是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。迄今为止,我撰写了超过 1,400 篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出 所有 Spring Boot 教程