Spring DI APIのためのQuarkusエクステンション
インジェクションにはCDIアノテーションを使用することが推奨されていますが、Quarkusでは、 spring-di
エクステンションという形で、Springのディペンデンシーインジェクションのための互換レイヤーを提供しています。
このガイドでは、Spring Frameworkに含まれる有名なDependency InjectionアノテーションをQuarkusアプリケーションでどのように活用するかを説明します。
前提条件
このガイドを完成させるには、以下が必要です:
-
約15分
-
IDE
-
JDK 11+ がインストールされ、
JAVA_HOME
が適切に設定されていること -
Apache Maven 3.9.3
-
使用したい場合は、 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プロジェクトの作成
まず、新しいプロジェクトが必要です。以下のコマンドで新規プロジェクトを作成します:
Windowsユーザーの場合:
-
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"
このコマンドは、プロジェクトを生成し、 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> {
}
インターフェイスが整ったところで、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.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
ブラウザで 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は注入ポイントのフィールド名またはパラメータ名に対する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 | 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のマッピングはありません。 |
Spring DI Configuration Reference
ビルド時に固定される設定プロパティ - その他の設定プロパティは実行時にオーバーライド可能です。
型 |
デフォルト |
|
---|---|---|
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: Show more |
boolean |
|