131. 功能 Bean 定义
对于需要快速启动的小型应用程序,Spring Cloud Function 支持“声明式” bean 声明样式。 bean 声明的功能样式是 Spring Framework 5.0 的功能,在 5.1 中进行了重大增强。
131.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);
}
}
您可以在无服务器平台(如 AWS Lambda 或 Azure Functions)中运行上述命令,也可以仅在 Classpath 中包含spring-cloud-function-starter-web
来在自己的 HTTP 服务器中运行上述命令。运行 main 方法将公开一个端点,您可以使用该端点 ping uppercase
函数:
$ curl localhost:8080 -d foo
FOO
spring-cloud-function-starter-web
中的 Web 适配器使用 Spring MVC,因此您需要一个 Servlet 容器。您也可以在默认服务器为 netty 的地方使用 Webflux(即使您仍然愿意使用 Servlet 容器也可以)-只需包含spring-cloud-starter-function-webflux
依赖项即可。功能相同,并且两者都可以使用用户应用程序代码。
现在介绍功能性 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(FunctionType.from(String.class).to(String.class)));
}
}
主要区别在于:
-
主要类是
ApplicationContextInitializer
。 -
@Bean
个方法已转换为对context.registerBean()
的调用 -
@SpringBootApplication
被@SpringBootConfiguration
代替,表示我们没有启用 Spring Boot 自动配置,但仍将类标记为“入口点”。 -
Spring Boot 中的
SpringApplication
已替换为 Spring Cloud Function 中的FunctionalSpringApplication
(它是子类)。
您在 Spring Cloud Function 应用程序中注册的业务逻辑 bean 的类型为FunctionRegistration
。这是一个包装,其中包含函数以及有关 Importing 和输出类型的信息。在应用程序的@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 uppercase(String value) {
return value.toUpperCase();
}
}
如果您添加单独的独立类型Function
的类并使用run()
方法的替代形式向SpringApplication
注册,它也将起作用。最主要的是,泛型类型信息可在运行时通过类声明获得。
如果您添加spring-cloud-starter-function-webflux
,则该应用程序将在其自己的 HTTP 服务器上运行(由于尚未实现嵌入式 Servlet 容器的功能形式,因此该应用程序目前无法与 MVC 启动器一起使用)。该应用程序还可以在 AWS Lambda 或 Azure Functions 中正常运行,并且启动时间的改善是惊人的。
Note
“精简型” Web 服务器对Function
签名的范围有一些限制-特别是它(目前)还不支持Message
Importing 和输出,但是 POJO 和任何类型的Publisher
应该可以。
131.2 测试功能应用程序
Spring Cloud Function 还具有一些用于集成测试的 Util,Spring Boot 用户将非常熟悉。例如,这是包装以上应用程序的 HTTP 服务器的集成测试:
@RunWith(SpringRunner.class)
@FunctionalSpringBootTest
@AutoConfigureWebTestClient
public class FunctionalTests {
@Autowired
private WebTestClient client;
@Test
public void words() throws Exception {
client.post().uri("/").body(Mono.just("foo"), String.class).exchange()
.expectStatus().isOk().expectBody(String.class).isEqualTo("FOO");
}
}
该测试几乎与您为同一应用的@Bean
版本编写的测试相同-唯一的区别是@FunctionalSpringBootTest
Comments,而不是常规@SpringBootTest
。其他所有部件(如@Autowired
WebTestClient
)都是标准的 Spring Boot 功能。
或者,您可以只使用FunctionCatalog
为非 HTTP 应用程序编写测试。例如:
@RunWith(SpringRunner.class)
@FunctionalSpringBootTest
public class FunctionalTests {
@Autowired
private FunctionCatalog catalog;
@Test
public void words() throws Exception {
Function<Flux<String>, Flux<String>> function = catalog.lookup(Function.class,
"function");
assertThat(function.apply(Flux.just("foo")).blockFirst()).isEqualTo("FOO");
}
}
(即使用户使用更简单的签名声明FunctionCatalog
,它们也总是从Flux
返回到Flux
的函数.)
131.3 功能 Bean 声明的局限性
与整个 Spring Boot 相比,大多数 Spring Cloud Function 应用程序的范围相对较小,因此我们能够轻松地使其适应这些功能 Bean 定义。如果您超出了有限的范围,则可以通过切换回@Bean
样式配置或使用混合方法来扩展 Spring Cloud Function 应用。例如,如果您想利用 Spring Boot 自动配置来与外部数据存储区集成,则需要使用@EnableAutoConfiguration
。如果需要,仍可以使用函数声明来定义函数(即“混合”样式),但是在这种情况下,您将需要使用spring.functional.enabled=false
明确关闭“全功能模式”,以便 Spring Boot 可以收回控制权。