Quarkus Extension for Spring DI API
虽然鼓励用户使用 CDI 注释进行注入,但是 Quarkus 以 spring-di
扩展的形式为 Spring 依赖注入提供了一个兼容性层。
While users are encouraged to use CDI annotations for injection, Quarkus provides a compatibility layer for Spring dependency injection in the form of the spring-di
extension.
本指南解释 Quarkus 应用程序如何利用 Spring 框架中包含的知名依赖注入注释。
This guide explains how a Quarkus application can leverage the well known Dependency Injection annotations included in the Spring Framework.
Solution
我们建议您遵循接下来的部分中的说明,按部就班地创建应用程序。然而,您可以直接跳到完成的示例。
We recommend that you follow the instructions in the next sections and create the application step by step. However, you can go right to the completed example.
克隆 Git 存储库: git clone {quickstarts-clone-url}
,或下载 {quickstarts-archive-url}[存档]。
Clone the Git repository: git clone {quickstarts-clone-url}
, or download an {quickstarts-archive-url}[archive].
该解决方案位于 spring-di-quickstart
directory 中。
The solution is located in the spring-di-quickstart
directory.
Creating the Maven project
首先,我们需要一个新项目。使用以下命令创建一个新项目:
First, we need a new project. Create a new project with the following command:
Unresolved directive in spring-di.adoc - include::{includes}/devtools/create-app.adoc[]
此命令生成一个导入 spring-di
扩展的项目。
This command generates a project which imports the spring-di
extension.
如果你已配置好 Quarkus 项目,那么你可以通过在项目基本目录中运行以下命令,向你的项目添加 spring-di
扩展:
If you already have your Quarkus project configured, you can add the spring-di
extension
to your project by running the following command in your project base directory:
Unresolved directive in spring-di.adoc - include::{includes}/devtools/extension-add.adoc[]
这会将以下内容添加到构建文件中:
This will add the following to your build file:
<dependency>
<groupId>io.quarkus</groupId>
<artifactId>quarkus-spring-di</artifactId>
</dependency>
implementation("io.quarkus:quarkus-spring-di")
Add beans using Spring annotations
让我们使用各种 Spring 注解创建一些 bean。
Let’s proceed to create some beans using various Spring annotations.
首先,我们将创建一个 StringFunction
接口,其中一些 bean 会实现该接口,稍后会将该接口注入另一个 bean 中。创建 src/main/java/org/acme/spring/di/StringFunction.java
文件,并设置以下内容:
First we will create a StringFunction
interface that some of our beans will implement and which will be injected into another bean later on.
Create a src/main/java/org/acme/spring/di/StringFunction.java
file and set the following content:
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
文件,内容如下:
With the interface in place, we will add an AppConfiguration
class which will use the Spring’s Java Config style for defining a bean.
It will be used to create a StringFunction
bean that will capitalize the text passed as parameter.
Create a src/main/java/org/acme/spring/di/AppConfiguration.java
file with the following content:
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
注解。
As a Spring developer, you might be tempted to add the @ComponentScan
annotation in order to define specific packages to scan for additional beans. Do note that @ComponentScan
is entirely unnecessary since Quarkus performs bean discovery only in annotated
mode with no visibility boundaries. Moreover, note that the bean discovery in Quarkus happens at build time.
In the same vein, Quarkus does not support the Spring @Import
annotation.
现在我们定义另一个 bean,它将使用 Spring 的构造型注解样式实现 StringFunction
。这个 bean 实际上是一个无操作 bean,它仅仅按原样返回输入。创建一个 src/main/java/org/acme/spring/di/NoOpSingleStringFunction.java
文件,并设置以下内容:
Now we define another bean that will implement StringFunction
using Spring’s stereotype annotation style.
This bean will effectively be a no-op bean that simply returns the input as is.
Create a src/main/java/org/acme/spring/di/NoOpSingleStringFunction.java
file and set the following content:
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
:
Quarkus also provides support for injecting configuration values using Spring’s @Value
annotation.
To see that in action, first edit the src/main/resources/application.properties
with the following content:
# Your configuration properties
greeting.message = hello
接下来,使用以下内容在 src/main/java/org/acme/spring/di/MessageProducer.java
中创建一个新的 Spring bean:
Next create a new Spring bean in src/main/java/org/acme/spring/di/MessageProducer.java
with the following content:
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
文件,并复制以下内容:
The final bean we will create ties together all the previous beans.
Create a src/main/java/org/acme/spring/di/GreeterBean.java
file and copy the following content:
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
,因此将使用该默认值。
In the code above, we see that both field injection and constructor injection are being used (note that constructor injection does not need the @Autowired
annotation since there is a single constructor).
Furthermore, the @Value
annotation on suffix
has also a default value defined, which in this case will be used since we have not defined greeting.suffix
in application.properties
.
Create the Jakarta REST resource
使用以下内容创建一个 src/main/java/org/acme/spring/di/GreeterResource.java
文件:
Create the src/main/java/org/acme/spring/di/GreeterResource.java
file with the following content:
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
方法的内容更改为:
We also need to update the functional test to reflect the changes made to the endpoint.
Edit the src/test/java/org/acme/spring/di/GreetingResourceTest.java
file and change the content of the testHelloEndpoint
method to:
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
使用以下内容运行应用程序:
Run the application with:
Unresolved directive in spring-di.adoc - include::{includes}/devtools/dev.adoc[]
用浏览器打开 [role="bare"][role="bare"]http://localhost:8080/greeting.
Open your browser to [role="bare"]http://localhost:8080/greeting.
结果应该是: HELLO WORLD!
The result should be: HELLO WORLD!
.
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。
Please note that the Spring support in Quarkus does not start a Spring Application Context nor are any Spring infrastructure classes run.
Spring classes and annotations are only used for reading metadata and / or are used as user code method return types or parameter types.
What that means for end users, is that adding arbitrary Spring libraries will not have any effect. Moreover, Spring infrastructure
classes (like org.springframework.beans.factory.config.BeanPostProcessor
, org.springframework.context.ApplicationContext
for example) will not be executed.
Regarding the dependency injection in particular, Quarkus uses a Dependency Injection mechanism (called ArC) based on the Jakarta Contexts and Dependency Injection 4.1 specification. If you want to learn more about it, we recommend you to read the Quarkus introduction to CDI and the CDI reference guide
The various Spring Boot test features are not supported by Quarkus. For testing purposes, please, check the Quarkus testing guide.
一些已知的限制:
Some known limitations:
-
In case of ambiguity, Spring uses a fallback match of the bean name against the injection point field name or parameter name. This is not supported, thus
@Named
annotation needs to be used to explicitly solve any ambiguity. -
Injecting all beans of a particular type is limited to
List<Bean>
. InjectingSet<Bean>
orMap<String, Bean>
is not supported. -
Optional injection using
@Autowired(required=false)
is not supported. Usejavax.enterprise.inject.Instance
and then testInstance.isResolvable()
. -
@Conditional
is ignored, since dependency injection gets resolved at build time. An alternative is to use conditional build profiles.
Conversion Table
下表显示了 Spring DI 注解可以如何转换为 CDI 和/或 MicroProfile 注解。
The following table shows how Spring DI annotations can be converted to CDI and / or MicroProfile annotations.
Spring | CDI / MicroProfile | Comments |
---|---|---|
@Autowired |
@Inject |
If the type is |
@Qualifier |
@Named |
|
@Value |
@ConfigProperty |
@ConfigProperty doesn’t support an expression language the way @Value does, but makes the typical use cases much easier to handle |
@Component |
@Singleton |
By default Spring stereotype annotations are singleton beans |
@Service |
@Singleton |
By default Spring stereotype annotations are singleton beans |
@Repository |
@Singleton |
By default Spring stereotype annotations are singleton beans |
@Configuration |
@ApplicationScoped |
In CDI a producer bean isn’t limited to the application scope, it could just as well be @Singleton or @Dependent |
@Bean |
@Produces |
|
@Scope |
Doesn’t have a one-to-one mapping to a CDI annotation. Depending on the value of @Scope, one of the @Singleton, @ApplicationScoped, @SessionScoped, @RequestScoped, @Dependent could be used |
|
@ComponentScan |
Doesn’t have a one-to-one mapping to a CDI annotation. It is not used in Quarkus because Quarkus does all classpath scanning at build time. |
|
@Import |
Doesn’t have a one-to-one mapping to a CDI annotation. |
Spring DI Configuration Reference
Unresolved directive in spring-di.adoc - include::{generated-dir}/config/quarkus-spring-di.adoc[]
More Spring guides
Quarkus 还有更多 Spring 兼容性功能。请参阅以下指南以获取更多详情:
Quarkus has more Spring compatibility features. See the following guides for more details: