Quarkus Extension for Spring DI API
虽然鼓励用户使用 CDI 注释进行注入,但是 Quarkus 以 spring-di
扩展的形式为 Spring 依赖注入提供了一个兼容性层。
本指南解释 Quarkus 应用程序如何利用 Spring 框架中包含的知名依赖注入注释。
Prerequisites
如要完成本指南,您需要:
-
Roughly 15 minutes
-
An IDE
-
安装了 JDK 17+,已正确配置
JAVA_HOME
-
Apache Maven ${proposed-maven-version}
-
如果你想使用 Quarkus CLI, 则可以选择使用
-
如果你想构建一个本机可执行文件(或如果你使用本机容器构建,则使用 Docker),则可以选择安装 Mandrel 或 GraalVM 以及 configured appropriately
Solution
我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。
克隆 Git 存储库: git clone $${quickstarts-base-url}.git
,或下载 $${quickstarts-base-url}/archive/main.zip[存档]。
该解决方案位于 spring-di-quickstart
directory 中。
Creating the Maven project
首先,我们需要一个新项目。使用以下命令创建一个新项目:
quarkus create app {create-app-group-id}:{create-app-artifact-id} \
--no-code
cd {create-app-artifact-id}
要创建一个 Gradle 项目,添加 --gradle
或 --gradle-kotlin-dsl
选项。
有关如何安装和使用 Quarkus CLI 的详细信息,请参见 Quarkus CLI 指南。
mvn {quarkus-platform-groupid}:quarkus-maven-plugin:{quarkus-version}:create \
-DprojectGroupId={create-app-group-id} \
-DprojectArtifactId={create-app-artifact-id} \
-DnoCode
cd {create-app-artifact-id}
要创建一个 Gradle 项目,添加 -DbuildTool=gradle
或 -DbuildTool=gradle-kotlin-dsl
选项。
适用于 Windows 用户:
-
如果使用 cmd,(不要使用反斜杠
\
,并将所有内容放在同一行上) -
如果使用 Powershell,将
-D
参数用双引号引起来,例如"-DprojectArtifactId={create-app-artifact-id}"
此命令生成一个导入 spring-di
扩展的项目。
如果你已配置好 Quarkus 项目,那么你可以通过在项目基本目录中运行以下命令,向你的项目添加 spring-di
扩展:
quarkus extension add {add-extension-extensions}
./mvnw quarkus:add-extension -Dextensions='{add-extension-extensions}'
./gradlew addExtension --extensions='{add-extension-extensions}'
这会将以下内容添加到构建文件中:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-di</artifactId>
</dependency>
implementation("io.quarkus:quarkus-spring-di")
Add beans using Spring annotations
让我们使用各种 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 配置样式定义一个 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 discovery,没有可见性边界。此外,请注意,Quarkus 中的 bean 发现发生在构建时。同样,Quarkus 不支持 Spring @Import
注解。
现在我们定义另一个 bean,它将使用 Spring 的构造型注解样式实现 StringFunction
。这个 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
注解注入配置值的 supports。要查看实际情况,首先使用以下内容编辑 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
,因此将使用该默认值。
Create the Jakarta REST resource
使用以下内容创建一个 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");
}
}
Update the test
我们还需要更新功能测试,以反映对端点的更改。编辑 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!"));
}
}
Package and run the application
使用以下内容运行应用程序:
quarkus dev
./mvnw quarkus:dev
./gradlew --console=plain quarkusDev
用浏览器打开 [role="bare"][role="bare"]http://localhost:8080/greeting.
结果应该是: HELLO WORLD!
Run the application as a native
你当然可以使用类似于 this 指南中的说明来创建一个本机映像。
Important Technical Note
请注意,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 introduction to CDI 和 CDI reference guide。Quarkus 不支持各种 Spring Boot 测试功能。对于测试目的,请查看 Quarkus testing guide。
一些已知的限制:
-
如果出现歧义,Spring 将使用 bean 名称与注入点字段名称或参数名称进行后备匹配。不支持此操作,因此需要使用
@Named
注解来明确解决任何歧义。 -
仅将某种特定类型的 bean 全部注入到
List<Bean>
有限。不支持Set<Bean>
或Map<String, Bean>
的注入。 -
不支持使用
@Autowired(required=false)
的可选注入。使用javax.enterprise.inject.Instance
,然后测试Instance.isResolvable()
。 -
忽略
@Conditional
,因为在构建时会解决依赖项注入。一种替代方案是使用 conditional build profiles。
Conversion Table
下表显示了 Spring DI 注解可以如何转换为 CDI 和/或 MicroProfile 注解。
Spring | CDI / MicroProfile | Comments |
---|---|---|
@Autowired |
@Inject |
如果类型为 |
@Qualifier |
@Named |
|
@Value |
@ConfigProperty |
@ConfigProperty 不支持表达式语言,但可以处理典型用例,使其更加容易处理 |
@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 Configuration Reference
Unresolved include directive in modules/ROOT/pages/spring-di.adoc - include::../../../target/quarkus-generated-doc/config/quarkus-spring-di.adoc[]
More Spring guides
Quarkus 还有更多 Spring 兼容性功能。请参阅以下指南以获取更多详情: