用于 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 项目
首先,我们需要一个新项目。使用以下命令创建一个新项目
对于 Windows 用户
-
如果使用 cmd,(不要使用反斜杠
\
并将所有内容放在同一行上) -
如果使用 Powershell,请将
-D
参数用双引号括起来,例如"-DprojectArtifactId=spring-di-quickstart"
此命令会生成一个项目,该项目导入 spring-di
扩展。
如果已配置 Quarkus 项目,则可以通过在项目基本目录中运行以下命令将 spring-di
扩展添加到项目中
quarkus extension add spring-di
./mvnw quarkus:add-extension -Dextensions='spring-di'
./gradlew addExtension --extensions='spring-di'
这会将以下内容添加到您的构建文件中
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-di</artifactId>
</dependency>
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!"));
}
}
打包并运行应用程序
使用以下命令运行应用程序
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
在浏览器中打开 https://:8080/greeting。
结果应为:HELLO WORLD!
。
将应用程序作为本地运行
您当然可以使用类似于此指南的说明来创建本机映像。
重要的技术说明
请注意,Quarkus 中的 Spring 支持不会启动 Spring 应用程序上下文,也不会运行任何 Spring 基础结构类。 Spring 类和注解仅用于读取元数据和/或用作用户代码方法返回类型或参数类型。 对于最终用户而言,这意味着添加任意 Spring 库不会有任何影响。 此外,Spring 基础结构类(例如 org.springframework.beans.factory.config.BeanPostProcessor
、org.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 |
如果类型为 |
@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 环境变量: 显示更多 |
布尔值 |
|