|
该版本仍在开发中,尚未被视为稳定。对于最新稳定版本,请使用 spring-cloud-function 5.0.0! |
功能豆定义
Spring Cloud Function 支持一种“功能性”风格的 bean 声明,适用于需要快速启动的小型应用。豆子声明的功能性样式是 Spring Framework 5.0 的一个功能,并在 5.1 版本中进行了重大改进。
比较功能性与传统Beans定义
这是一个原生的 Spring Cloud 函数应用,来自
熟悉@Configuration和@Bean宣言风格:
@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);
}
}
现在说说功能性:用户应用代码可以重写为“功能性” 形式,如下:
@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)));
}
}
主要区别如下:
-
主要类是
应用上下文初始化器. -
这
@Bean方法已被转换为调用context.registerBean() -
这
@SpringBootApplication已被替换为@SpringBootConfiguration以示我们没有助长Spring 启动自动配置,但仍然标记该类为“条目” “要点”。 -
这
SpringApplication来自春季靴子的名称已被替换为FunctionalSpringApplication来自Spring Cloud Function(这是一个 子职业)。
你在 Spring Cloud Function 应用中注册的业务逻辑豆具有 类型功能注册.
这是一个包含函数以及输入和输出类型信息的封装器。在@Bean该应用形式中,该信息可以通过反射方式推导,但在功能性豆注册中,有些部分
除非我们使用功能注册.
一种替代使用应用上下文初始化器和功能注册就是要做出应用
自身实现功能(或消费者或提供商).示例(相当于上述):
@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();
}
}
如果你添加一个独立的类型类,也能正常工作功能并注册为
这SpringApplication使用另一种形式run()方法。最重要的是,通用药
类型信息可通过类声明在运行时获得。
假设你有
@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));
}
功能豆宣言的局限性
大多数 Spring Cloud Function 应用的范围相较于 Spring Boot 整体来说相对较小,
因此,我们能够轻松地将其适应这些功能性Beans定义。如果你跳出那个有限的范围,
你可以切换回@Bean样式配置,或使用混合配置
方法。如果你想利用 Spring Boot 的自动配置来与外部数据存储集成,
例如,你需要使用@EnableAutoConfiguration.你的函数仍然可以用函数定义
如果你愿意,也可以选择声明(即“混合”风格),但在这种情况下,你需要明确关闭“完整”
功能模式“使用spring.functional.enabled=false这样Spring靴才能重新掌控一切。
功能可视化与控制
Spring Cloud Function 支持对以下功能进行可视化功能目录通过执行器端点以及程序化方式。
程序化方式
要在你的应用上下文中看到功能,程序化地显示,你只需要访问功能目录.你可以
查找获取目录大小的方法,查找函数,并列出所有可用功能的名称。
例如
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框架添加依赖:
<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>
你还必须启用功能执行器端点通过设置以下属性实现:--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());
}
}
或者当使用函数豆定义风格时:
@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同一应用的版本——唯一的区别
是@FunctionalSpringBootTest注释,而不是常规@SpringBootTest.其他所有的碎片,
比如@Autowired 测试休息模板,是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.3.1-SNAPSHOT</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>
或者你可以为非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");
}
}