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

Spring DI APIのためのQuarkusエクステンション

インジェクションにはCDIアノテーションを使用することが推奨されていますが、Quarkusでは、 spring-di エクステンションという形で、Springのディペンデンシーインジェクションのための互換レイヤーを提供しています。

このガイドでは、Spring Frameworkに含まれる有名なDependency InjectionアノテーションをQuarkusアプリケーションでどのように活用するかを説明します。

前提条件

このガイドを完成させるには、以下が必要です:

  • 約15分

  • IDE

  • JDK 11+ がインストールされ、 JAVA_HOME が適切に設定されていること

  • Apache Maven 3.8.6

  • 使用したい場合は、 Quarkus CLI

  • ネイティブ実行可能ファイルをビルドしたい場合、MandrelまたはGraalVM(あるいはネイティブなコンテナビルドを使用する場合はDocker)をインストールし、 適切に設定していること

ソリューション

次のセクションで紹介する手順に沿って、ステップを踏んでアプリを作成することをお勧めします。ただし、完成した例にそのまま進んでも構いません。

Gitレポジトリをクローンするか git clone https://github.com/quarkusio/quarkus-quickstarts.gitアーカイブ をダウンロードします。

このソリューションは spring-di-quickstart ディレクトリ にあります。

Mavenプロジェクトの作成

まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します:

コマンドラインインタフェース
quarkus create app org.acme:spring-di-quickstart \
    --extension='resteasy-reactive,spring-di' \
    --no-code
cd spring-di-quickstart

Gradleプロジェクトを作成するには、 --gradle または --gradle-kotlin-dsl オプションを追加します。

Quarkus CLIのインストール方法や使用方法については、<a href="cli-tooling.html">Quarkus CLIガイド</a> を参照してください。

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

Gradleプロジェクトを作成するには、 -DbuildTool=gradle または -DbuildTool=gradle-kotlin-dsl オプションを追加します。

このコマンドは、プロジェクトを生成し、 spring-di エクステンションをインポートします。

すでにQuarkusプロジェクトが設定されている場合は、プロジェクトのベースディレクトリで以下のコマンドを実行することで、 spring-di エクステンションをプロジェクトに追加することができます:

コマンドラインインタフェース
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> {

}

インターフェイスが整ったところで、SpringのJava Configスタイルを使ってBeanを定義する AppConfiguration クラスを追加します。これを使って、パラメータとして渡されたテキストを大文字にする StringFunction 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の開発者であれば、追加のBeanをスキャンする特定のパッケージを定義するために、 @ComponentScan アノテーションを追加したいと思うかもしれません。しかし、Quarkusは可視性の境界がない annotated モードでのみ Beanディスカバリー を行うため、 @ComponentScan は全く必要ないことに注意してください。さらに、QuarkusでのBeanディスカバリーはビルド時に行われることに注意してください。同じように、QuarkusはSpringの @Import アノテーションをサポートしていません。

次に、Springのステレオタイプアノテーションスタイルを使用して、 StringFunction を実装する別のBeanを定義します。このBeanは、事実上、入力をそのまま返すだけのno-op 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);
    }
}

上のコードでは、フィールドインジェクションとコンストラクタインジェクションの両方が使用されていることがわかります(コンストラクタインジェクションでは、コンストラクタが 1 つなので、 @Autowired のアノテーションは必要ないことに注意してください)。さらに、 suffix@Value アノテーションには、デフォルト値も定義されています。この場合、 application.propertiesgreeting.suffix を定義していないので、これが使用されます。

JAX-RSリソースの作成

src/main/java/org/acme/spring/di/GreeterResource.java のファイルを以下の内容で作成します:

package org.acme.spring.di;

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

import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.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
Maven
./mvnw quarkus:dev
Gradle
./gradlew --console=plain quarkusDev

ブラウザで http://localhost:8080/greeting を開きます。

結果は次のようになります: HELLO WORLD!.

アプリケーションをネイティブバイナリーとして実行

もちろん、 この ガイドと同様の手順でネイティブイメージを作成することもできます。

重要な技術的注意点

QuarkusのSpringサポートでは、Spring Application Contextは起動せず、Springインフラストラクチャクラスも実行されないことに注意してください。Springのクラスやアノテーションは、メタデータを読み込んだり、ユーザーコードのメソッドの戻り値の型やパラメータの型として使用されるだけです。エンドユーザーにとっての意味は、任意のSpringライブラリを追加しても何の効果もないということです。さらに、Springのインフラストラクチャクラス(例えば、 org.springframework.beans.factory.config.BeanPostProcessor, org.springframework.context.ApplicationContext のような)は実行されません。特にディペンデンシーインジェクションについては、Quarkusは Contexts and Dependency Injection for Java 2.0 仕様に基づいたディペンデンシーインジェクションの仕組み(ArCと呼ばれる)を使用しています。Spring Bootの様々なテスト機能は、Quarkusではサポートされていません。テスト目的の場合は、 Quarkusのテストガイド をご確認ください。

変換テーブル

以下の表は、Spring DIのアノテーションをCDIおよび/またはMicroProfileのアノテーションに変換する方法を示しています。

Spring CDI / MicroProfile Comments

@Autowired

@Inject

@Qualifier

@Named

@Value

@ConfigProperty

ConfigurationProperty は、@Value とは違って式言語をサポートしていませんが、典型的な使用例をはるかに簡単に扱うことができます

@Component

@Singleton

デフォルトでは、SpringのステレオタイプアノテーションはシングルトンBeanです

@Service

@Singleton

デフォルトでは、SpringのステレオタイプアノテーションはシングルトンBeanです

@Repository

@Singleton

デフォルトでは、SpringのステレオタイプアノテーションはシングルトンBeanです

@Configuration

@ApplicationScoped

CDIでは、プロデューサBeanはアプリケーションスコープに限定されず、@Singletonや@Dependentにすることもできます

@Bean

@Produces

@Scope

CDI アノテーションへの 1 対 1 のマッピングはありません。Scope の値に応じて、@Singleton、@ApplicationScoped、@SessionScoped、@RequestScoped、@Dependent のいずれかが使用されます

@ComponentScan

CDIアノテーションへの1対1のマッピングはありません。Quarkusはビルド時にすべてのクラスパスのスキャンを行うため、Quarkusでは使用されません。

@Import

CDIアノテーションとの1対1のマッピングはありません。