The English version of quarkus.io is the official project site. Translated sites are community supported on a best-effort basis.

Quarkus Extension for Spring DI API

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.

This guide explains how a Quarkus application can leverage the well known Dependency Injection annotations included in the Spring Framework.

先决条件

完成这个指南,你需要:

  • 大概15分钟

  • 编辑器

  • JDK 17+ installed with JAVA_HOME configured appropriately

  • Apache Maven 3.9.9

  • 如果你愿意的话,还可以选择使用Quarkus CLI

  • 如果你想构建原生可执行程序,可以选择安装Mandrel或者GraalVM,并正确配置(或者使用Docker在容器中进行构建)

解决方案

我们建议您按照下一节的说明逐步创建应用程序。然而,您可以直接转到已完成的示例。

克隆 Git 仓库: git clone https://github.com/quarkusio/quarkus-quickstarts.git ,或下载一个 存档

The solution is located in the spring-di-quickstart directory.

创建Maven项目

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

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

创建Grade项目,请添加 --gradle 或者 --gradle-kotlin-dsl 参数。

For more information about how to install and use the Quarkus CLI, see the Quarkus CLI guide.

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

创建Grade项目,请添加 -DbuildTool=gradle 或者 -DbuildTool=gradle-kotlin-dsl 参数。

For Windows users:

  • If using cmd, (don’t use backward slash \ and put everything on the same line)

  • If using Powershell, wrap -D parameters in double quotes e.g. "-DprojectArtifactId=spring-di-quickstart"

This command generates a project which imports the spring-di extension.

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:

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")

Add beans using Spring annotations

Let’s proceed to create some beans using various Spring annotations.

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> {

}

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;
    }
}

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.

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 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

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;
    }
}

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);
    }
}

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

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

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!"));
    }

}

打包并运行该应用程序

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

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

在你的浏览器中打开 http://localhost:8080/greeting 网页。

The result should be: HELLO WORLD!.

Run the application as a native

You can of course create a native image using instructions similar to this 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>. Injecting Set<Bean> or Map<String, Bean> is not supported.

  • Optional injection using @Autowired(required=false) is not supported. Use javax.enterprise.inject.Instance and then test Instance.isResolvable().

  • @Conditional is ignored, since dependency injection gets resolved at build time. An alternative is to use conditional build profiles.

Conversion Table

The following table shows how Spring DI annotations can be converted to CDI and / or MicroProfile annotations.

Spring CDI / MicroProfile 备注

@Autowired

@Inject

If the type is java.util.List, the io.quarkus.arc.All qualifier is added.

@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

Configuration property fixed at build time - All other configuration properties are overridable at runtime

Configuration property

类型

默认值

Whether Spring DI is enabled **during the build**.

Turning this setting off will result in Quarkus completely ignoring beans annotated with Spring annotations

Environment variable: QUARKUS_SPRING_DI_ENABLED

Show more

boolean

true

Related content