编辑此页面

用于 Spring DI API 的 Quarkus 扩展

虽然鼓励用户使用 CDI 注解进行注入,但 Quarkus 以 spring-di 扩展的形式为 Spring 依赖注入提供了一个兼容层。

本指南解释了 Quarkus 应用程序如何利用 Spring Framework 中包含的众所周知的依赖注入注解。

先决条件

要完成本指南,您需要

  • 大约 15 分钟

  • 一个 IDE

  • 已安装 JDK 17+ 并正确配置了 JAVA_HOME

  • Apache Maven 3.9.9

  • 如果您想使用它,可以选择 Quarkus CLI

  • 如果您想构建本机可执行文件(或者如果您使用本机容器构建,则为 Docker),可以选择安装 Mandrel 或 GraalVM 并进行适当的配置

解决方案

我们建议您按照以下章节中的说明,逐步创建应用程序。但是,您可以直接转到完整的示例。

克隆 Git 仓库:git clone https://github.com/quarkusio/quarkus-quickstarts.git,或下载 归档文件

解决方案位于 spring-di-quickstart 目录中。

创建 Maven 项目

首先,我们需要一个新项目。使用以下命令创建一个新项目

CLI
quarkus create app org.acme:spring-di-quickstart \
    --extension='rest,spring-di' \
    --no-code
cd spring-di-quickstart

要创建 Gradle 项目,请添加 --gradle--gradle-kotlin-dsl 选项。

有关如何安装和使用 Quarkus CLI 的更多信息,请参阅 Quarkus CLI 指南。

Maven
mvn io.quarkus.platform:quarkus-maven-plugin:3.24.4:create \
    -DprojectGroupId=org.acme \
    -DprojectArtifactId=spring-di-quickstart \
    -Dextensions='rest,spring-di' \
    -DnoCode
cd spring-di-quickstart

要创建 Gradle 项目,请添加 -DbuildTool=gradle-DbuildTool=gradle-kotlin-dsl 选项。

对于 Windows 用户

  • 如果使用 cmd,(不要使用反斜杠 \ 并将所有内容放在同一行上)

  • 如果使用 Powershell,请将 -D 参数用双引号括起来,例如 "-DprojectArtifactId=spring-di-quickstart"

此命令会生成一个项目,该项目导入 spring-di 扩展。

如果已配置 Quarkus 项目,则可以通过在项目基本目录中运行以下命令将 spring-di 扩展添加到项目中

CLI
quarkus extension add spring-di
Maven
./mvnw quarkus:add-extension -Dextensions='spring-di'
Gradle
./gradlew addExtension --extensions='spring-di'

这会将以下内容添加到您的构建文件中

pom.xml
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-spring-di</artifactId>
</dependency>
build.gradle
implementation("io.quarkus:quarkus-spring-di")

使用 Spring 注解添加 Bean

让我们继续使用各种 Spring 注解创建一些 Bean。

首先,我们将创建一个 StringFunction 接口,我们的一些 Bean 将实现该接口,并在稍后注入到另一个 Bean 中。 创建一个 src/main/java/org/acme/spring/di/StringFunction.java 文件并设置以下内容

package org.acme.spring.di;

import java.util.function.Function;

public interface StringFunction extends Function<String, String> {

}

有了接口后,我们将添加一个 AppConfiguration 类,该类将使用 Spring 的 Java Config 样式来定义 Bean。它将用于创建一个 StringFunction Bean,该 Bean 将文本大写作为参数传递。创建一个 src/main/java/org/acme/spring/di/AppConfiguration.java 文件,其中包含以下内容

package org.acme.spring.di;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfiguration {

    @Bean(name = "capitalizeFunction")
    public StringFunction capitalizer() {
        return String::toUpperCase;
    }
}

作为 Spring 开发人员,您可能会想添加 @ComponentScan 注解,以便定义要扫描其他 Bean 的特定包。 请注意,@ComponentScan 完全没有必要,因为 Quarkus 仅在 annotated 模式下执行 Bean 发现,且没有可见性边界。 此外,请注意,Quarkus 中的 Bean 发现发生在构建时。 同样,Quarkus 不支持 Spring @Import 注解。

现在,我们定义另一个将使用 Spring 的构造型注解样式来实现 StringFunction 的 Bean。 此 Bean 实际上将是一个无操作 Bean,它只是按原样返回输入。 创建一个 src/main/java/org/acme/spring/di/NoOpSingleStringFunction.java 文件并设置以下内容

package org.acme.spring.di;

import org.springframework.stereotype.Component;

@Component("noopFunction")
public class NoOpSingleStringFunction implements StringFunction {

    @Override
    public String apply(String s) {
        return s;
    }
}

Quarkus 还提供对使用 Spring 的 @Value 注解注入配置值的支持。 要看到实际效果,首先使用以下内容编辑 src/main/resources/application.properties

# Your configuration properties
greeting.message = hello

接下来,在 src/main/java/org/acme/spring/di/MessageProducer.java 中创建一个新的 Spring Bean,其中包含以下内容

package org.acme.spring.di;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class MessageProducer {

    @Value("${greeting.message}")
    String message;

    public String getPrefix() {
        return message;
    }
}

我们将创建的最终 Bean 将所有先前的 Bean 绑定在一起。 创建一个 src/main/java/org/acme/spring/di/GreeterBean.java 文件,然后复制以下内容

package org.acme.spring.di;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class GreeterBean {

    private final MessageProducer messageProducer;

    @Autowired
    @Qualifier("noopFunction")
    StringFunction noopStringFunction;

    @Autowired
    @Qualifier("capitalizeFunction")
    StringFunction capitalizerStringFunction;

    @Value("${greeting.suffix:!}")
    String suffix;

    public GreeterBean(MessageProducer messageProducer) {
        this.messageProducer = messageProducer;
    }

    public String greet(String name) {
        final String initialValue = messageProducer.getPrefix() + " " + name + suffix;
        return noopStringFunction.andThen(capitalizerStringFunction).apply(initialValue);
    }
}

在上面的代码中,我们看到正在使用字段注入和构造函数注入(请注意,由于只有一个构造函数,因此构造函数注入不需要 @Autowired 注解)。 此外,suffix 上的 @Value 注解也定义了默认值,在这种情况下将使用该值,因为我们没有在 application.properties 中定义 greeting.suffix

创建 Jakarta REST 资源

创建 src/main/java/org/acme/spring/di/GreeterResource.java 文件,其中包含以下内容

package org.acme.spring.di;

import org.springframework.beans.factory.annotation.Autowired;

import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/greeting")
public class GreeterResource {

    @Autowired
    GreeterBean greeterBean;

    @GET
    @Produces(MediaType.TEXT_PLAIN)
    public String hello() {
        return greeterBean.greet("world");
    }
}

更新测试

我们还需要更新功能测试以反映对端点所做的更改。 编辑 src/test/java/org/acme/spring/di/GreetingResourceTest.java 文件,然后将 testHelloEndpoint 方法的内容更改为

import io.quarkus.test.junit.QuarkusTest;
import org.junit.jupiter.api.Test;

import static io.restassured.RestAssured.given;
import static org.hamcrest.CoreMatchers.is;

@QuarkusTest
public class GreetingResourceTest {

    @Test
    public void testHelloEndpoint() {
        given()
            .when().get("/greeting")
            .then()
                .statusCode(200)
                .body(is("HELLO WORLD!"));
    }

}

打包并运行应用程序

使用以下命令运行应用程序

CLI
quarkus dev
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

在浏览器中打开 https://:8080/greeting

结果应为:HELLO WORLD!

将应用程序作为本地运行

您当然可以使用类似于指南的说明来创建本机映像。

重要的技术说明

请注意,Quarkus 中的 Spring 支持不会启动 Spring 应用程序上下文,也不会运行任何 Spring 基础结构类。 Spring 类和注解仅用于读取元数据和/或用作用户代码方法返回类型或参数类型。 对于最终用户而言,这意味着添加任意 Spring 库不会有任何影响。 此外,Spring 基础结构类(例如 org.springframework.beans.factory.config.BeanPostProcessororg.springframework.context.ApplicationContext)将不会执行。 特别是在依赖注入方面,Quarkus 使用一种基于 Jakarta Contexts and Dependency Injection 4.1 规范的依赖注入机制(称为 ArC)。 如果您想了解更多信息,我们建议您阅读 Quarkus CDI 简介CDI 参考指南。 Quarkus 不支持各种 Spring Boot 测试功能。 对于测试目的,请查看 Quarkus 测试指南

一些已知的限制

  • 如果存在歧义,Spring 会使用 Bean 名称与注入点字段名称或参数名称的回退匹配。 这不受支持,因此需要使用 @Named 注解来显式解决任何歧义。

  • 注入特定类型的全部 Bean 仅限于 List<Bean>。 不支持注入 Set<Bean>Map<String, Bean>

  • 不支持使用 @Autowired(required=false) 的可选注入。 请使用 javax.enterprise.inject.Instance,然后测试 Instance.isResolvable()

  • @Conditional 将被忽略,因为依赖注入在构建时得到解决。 一种替代方法是使用 条件构建配置文件

转换表

下表显示了如何将 Spring DI 注解转换为 CDI 和/或 MicroProfile 注解。

Spring CDI / MicroProfile 注释

@Autowired

@Inject

如果类型为 java.util.List,则会添加 io.quarkus.arc.All 限定符。

@Qualifier

@Named

@Value

@ConfigProperty

@ConfigProperty 不支持像 @Value 那样的表达式语言,但可以更轻松地处理典型的用例

@Component

@Singleton

默认情况下,Spring 构造型注解是单例 Bean

@Service

@Singleton

默认情况下,Spring 构造型注解是单例 Bean

@Repository

@Singleton

默认情况下,Spring 构造型注解是单例 Bean

@Configuration

@ApplicationScoped

在 CDI 中,生产者 Bean 不限于应用程序范围,它也可以是 @Singleton 或 @Dependent

@Bean

@Produces

@Scope

没有与 CDI 注解的一对一映射。 根据 @Scope 的值,可以使用 @Singleton、@ApplicationScoped、@SessionScoped、@RequestScoped、@Dependent 中的一个

@ComponentScan

没有与 CDI 注解的一对一映射。 它在 Quarkus 中未使用,因为 Quarkus 在构建时执行所有类路径扫描。

@Import

没有与 CDI 注解的一对一映射。

Spring DI 配置参考

构建时固定的配置属性 - 所有其他配置属性都可以在运行时覆盖

配置属性

类型

默认

是否在**构建期间**启用 Spring DI。

关闭此设置将导致 Quarkus 完全忽略使用 Spring 注解注解的 Bean

环境变量:QUARKUS_SPRING_DI_ENABLED

显示更多

布尔值

true

相关内容