関数 Bean の定義
Spring Cloud Function は、高速起動が必要な小さなアプリ向けに、「関数」スタイルの Bean 宣言をサポートしています。Bean 宣言の機能スタイルは、5.1 が大幅に強化された Spring Framework 5.0 の機能でした。
関数と従来の Bean 定義の比較
これは、おなじみの @Configuration
および @Bean
宣言スタイルを備えたバニラ Spring Cloud Function アプリケーションです。
@SpringBootApplication
public class DemoApplication {
@Bean
public Function<String, String> uppercase() {
return value -> value.toUpperCase();
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
関数 Bean の場合: ユーザーアプリケーションコードは、次のように「関数」形式に再キャストできます。
@SpringBootConfiguration
public class DemoApplication implements ApplicationContextInitializer<GenericApplicationContext> {
public static void main(String[] args) {
FunctionalSpringApplication.run(DemoApplication.class, args);
}
public Function<String, String> uppercase() {
return value -> value.toUpperCase();
}
@Override
public void initialize(GenericApplicationContext context) {
context.registerBean("demo", FunctionRegistration.class,
() -> new FunctionRegistration<>(uppercase())
.type(FunctionTypeUtils.functionType(String.class, String.class)));
}
}
主な違いは次のとおりです。
メインクラスは
ApplicationContextInitializer
です。@Bean
メソッドはcontext.registerBean()
への呼び出しに変換されました@SpringBootApplication
は@SpringBootConfiguration
に置き換えられ、Spring Boot 自動構成を有効にしていないことを示していますが、クラスは「エントリポイント」としてマークされています。Spring Boot の
SpringApplication
は、Spring Cloud Function のFunctionalSpringApplication
に置き換えられました(これはサブクラスです)。
Spring Cloud Function アプリに登録するビジネスロジック Bean の型は FunctionRegistration
です。これは、関数と、入力型と出力型に関する情報の両方を含むラッパーです。アプリケーションの @Bean
形式では、その情報は反射的に取得できますが、関数 Bean 登録では、FunctionRegistration
を使用しない限り、その一部が失われます。
ApplicationContextInitializer
および FunctionRegistration
を使用する代わりに、アプリケーション自体に Function
(または Consumer
または Supplier
)を実装させることもできます。例(上記と同等):
@SpringBootConfiguration
public class DemoApplication implements Function<String, String> {
public static void main(String[] args) {
FunctionalSpringApplication.run(DemoApplication.class, args);
}
@Override
public String apply(String value) {
return value.toUpperCase();
}
}
型 Function
の別個のスタンドアロンクラスを追加し、run()
メソッドの代替形式を使用して SpringApplication
に登録する場合にも機能します。主なことは、ジェネリクス型の情報は、実行時にクラス宣言を通じて利用できるということです。
持っているとしましょう
@Component
public class CustomFunction implements Function<Flux<Foo>, Flux<Bar>> {
@Override
public Flux<Bar> apply(Flux<Foo> flux) {
return flux.map(foo -> new Bar("This is a Bar object from Foo value: " + foo.getValue()));
}
}
それをそのように登録します:
@Override
public void initialize(GenericApplicationContext context) {
context.registerBean("function", FunctionRegistration.class,
() -> new FunctionRegistration<>(new CustomFunction()).type(CustomFunction.class));
}
関数 Bean 宣言の制限
ほとんどの Spring Cloud Function アプリは、Spring Boot 全体に比べてスコープが比較的小さいため、これらの関数 Bean 定義に簡単に適合させることができます。その限られた範囲を超えた場合は、@Bean
スタイルの構成に戻すか、ハイブリッドアプローチを使用して、Spring Cloud Function アプリを継承できます。たとえば、外部データストアとの統合に Spring Boot 自動構成を利用する場合は、@EnableAutoConfiguration
を使用する必要があります。必要に応じて関数宣言を使用して関数を定義することもできますが(つまり、「ハイブリッド」スタイル)、その場合は、Spring Boot が制御を取り戻すことができるように、spring.functional.enabled=false
を使用して「完全関数モード」を明示的にオフにする必要があります。
機能の視覚化と制御
Spring Cloud Function は、アクチュエーターエンドポイントおよびプログラムによる方法で FunctionCatalog
で使用可能な機能の視覚化をサポートします。
プログラム的な方法
プログラムでアプリケーションコンテキスト内で使用可能な機能を確認するには、FunctionCatalog
にアクセスするだけです。ここでは、カタログのサイズを取得するためのメソッド、ルックアップ関数、使用可能なすべての関数の名前を一覧表示できます。
以下に例を示します。
FunctionCatalog functionCatalog = context.getBean(FunctionCatalog.class);
int size = functionCatalog.size(); // will tell you how many functions available in catalog
Set<String> names = functionCatalog.getNames(null); will list the names of all the Function, Suppliers and Consumers available in catalog
. . .
アクチュエーター
アクチュエーターと Web はオプションであるため、最初に Web 依存関係の 1 つを追加し、アクチュエーター依存関係を手動で追加する必要があります。次の例は、Web フレームワークの依存関係を追加する方法を示しています。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
次の例は、WebFlux フレームワークの依存関係を追加する方法を示しています。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
次のように、アクチュエーターの依存関係を追加できます。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
また、次のプロパティを設定して、functions
アクチュエーターのエンドポイントを有効にする必要があります: --management.endpoints.web.exposure.include=functions
。
次の URL にアクセスして、FunctionCatalog の機能を確認してください。<host>:<port>/actuator/functions (英語)
以下に例を示します。
curl http://localhost:8080/actuator/functions
出力は次のようになります。
{"charCounter":
{"type":"FUNCTION","input-type":"string","output-type":"integer"},
"logger":
{"type":"CONSUMER","input-type":"string"},
"functionRouter":
{"type":"FUNCTION","input-type":"object","output-type":"object"},
"words":
{"type":"SUPPLIER","output-type":"string"}. . .
関数アプリケーションのテスト
Spring Cloud Function には、Spring Boot ユーザーにとって非常に馴染みのある統合テスト用のユーティリティもいくつかあります。
これがアプリケーションであるとします。
@SpringBootApplication
public class SampleFunctionApplication {
public static void main(String[] args) {
SpringApplication.run(SampleFunctionApplication.class, args);
}
@Bean
public Function<String, String> uppercase() {
return v -> v.toUpperCase();
}
}
このアプリケーションをラップする HTTP サーバーの統合テストは次のとおりです。
@SpringBootTest(classes = SampleFunctionApplication.class,
webEnvironment = WebEnvironment.RANDOM_PORT)
public class WebFunctionTests {
@Autowired
private TestRestTemplate rest;
@Test
public void test() throws Exception {
ResponseEntity<String> result = this.rest.exchange(
RequestEntity.post(new URI("/uppercase")).body("hello"), String.class);
System.out.println(result.getBody());
}
}
または、関数 Bean 定義スタイルが使用されている場合:
@FunctionalSpringBootTest
public class WebFunctionTests {
@Autowired
private TestRestTemplate rest;
@Test
public void test() throws Exception {
ResponseEntity<String> result = this.rest.exchange(
RequestEntity.post(new URI("/uppercase")).body("hello"), String.class);
System.out.println(result.getBody());
}
}
このテストは、同じアプリの @Bean
バージョンに対して作成するテストとほぼ同じです。唯一の違いは、通常の @SpringBootTest
ではなく @FunctionalSpringBootTest
アノテーションです。@Autowired
TestRestTemplate
のような他のすべての部品は、標準の Spring Boot 機能です。
そして、ここで正しい依存関係を支援するために、POM からの抜粋があります
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<relativePath/> <!-- lookup parent from repository -->
</parent>
. . . .
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-function-web</artifactId>
<version>4.1.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
または、FunctionCatalog
のみを使用して非 HTTP アプリのテストを作成することもできます。例:
@FunctionalSpringBootTest
public class FunctionalTests {
@Autowired
private FunctionCatalog catalog;
@Test
public void words() {
Function<String, String> function = catalog.lookup(Function.class,
"uppercase");
assertThat(function.apply("hello")).isEqualTo("HELLO");
}
}