|
请使用 spring-cloud-function 5.0.1 获取最新稳定版本! |
功能型 Bean 定义
Spring Cloud Function支持一种“函数式”风格的bean声明,适用于小型应用程序,其中需要快速启动。这是Spring Framework 5.0中的一个新功能,在5.1中又进行了重大增强。
功能与传统 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);
}
}
现在来看功能型 beans:用户应用程序代码可以转换为"功能型"形式,例如如下:
@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。 -
请求被拒绝了,您没有权限访问此页面。
-
@SpringBootApplication已被替换为@SpringBootConfiguration,以表明我们没有启用Spring Boot自动配置,但仍将该类标记为“入口点”。 -
SpringApplication由 Spring Boot 提供的已由 Spring Cloud Function 提供的FunctionalSpringApplication取代(它是其子类)。
在 Spring Cloud Function 应用中注册的业务逻辑 bean 的类型为 FunctionRegistration。 这是包含函数以及输入和输出类型信息的包装器。 在 @Bean 形式的应用程序中,这些信息可以通过反射推断出来,但在功能 bean 注册中,如果不使用 FunctionRegistration,则会丢失某些信息。
另一种方法是让应用程序本身实现 Function(或3 或4 )。 例如(等效于上面的示例):
@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));
}
<h2>功能型 bean 声明的限制</h2>
大多数 Spring Cloud Function 应用的范围相对较小,与整个 Spring Boot 相比,所以我们能够轻松地适应这些函数式 bean 定义。如果您超出了这个有限的范围,那么您可以切换回@Bean样式的配置,或者使用混合方法来扩展您的 Spring Cloud Function 应用。如果您想利用 Spring Boot 自动配置来集成外部数据库存储,例如,您需要使用@EnableAutoConfiguration。即使您希望使用函数式声明(即“混合”样式),您的函数仍然可以使用函数式定义,但这种情况下,您需要使用spring.functional.enabled=false显式关闭“完全函数式模式”,以便 Spring Boot 可以重新获得控制权。
功能可视化和控制
Spring Cloud Function支持通过Actuator端点以及程序化方式查看在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 依赖项之一以及启动程序依赖项。下面的例子展示了如何为 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>
您可以按如下方式添加Actuator依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
还必须通过设置以下属性来启用functions执行器端点: --management.endpoints.web.exposure.include=functions。
访问以下网址查看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());
}
}
This test is almost identical to the one you would write for the @Bean version of the same app - the only difference is the @FunctionalSpringBootTest annotation, instead of the regular @SpringBootTest. All the other pieces, like the @Autowired TestRestTemplate, are standard Spring Boot features.
And to help with correct dependencies here is the excerpt from 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.6</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");
}
}