请使用 spring-cloud-function 5.0.1 获取最新稳定版本!spring-doc.cadn.net.cn

功能型 Bean 定义

Spring Cloud Function支持一种“函数式”风格的bean声明,适用于小型应用程序,其中需要快速启动。这是Spring Framework 5.0中的一个新功能,在5.1中又进行了重大增强。spring-doc.cadn.net.cn

功能与传统 bean 定义的比较

这里是使用熟悉 @Configuration@Bean 声明风格的原始 Spring Cloud Function 应用程序:spring-doc.cadn.net.cn

@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:用户应用程序代码可以转换为"功能型"形式,例如如下:spring-doc.cadn.net.cn

@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)));
  }

}

主要区别是:spring-doc.cadn.net.cn

  • 主要类是ApplicationContextInitializerspring-doc.cadn.net.cn

  • 请求被拒绝了,您没有权限访问此页面。spring-doc.cadn.net.cn

  • @SpringBootApplication已被替换为@SpringBootConfiguration,以表明我们没有启用Spring Boot自动配置,但仍将该类标记为“入口点”。spring-doc.cadn.net.cn

  • SpringApplication 由 Spring Boot 提供的已由 Spring Cloud Function 提供的FunctionalSpringApplication取代(它是其子类)。spring-doc.cadn.net.cn

在 Spring Cloud Function 应用中注册的业务逻辑 bean 的类型为 FunctionRegistration。 这是包含函数以及输入和输出类型信息的包装器。 在 @Bean 形式的应用程序中,这些信息可以通过反射推断出来,但在功能 bean 注册中,如果不使用 FunctionRegistration,则会丢失某些信息。spring-doc.cadn.net.cn

另一种方法是让应用程序本身实现 Function(或3 4 )。 例如(等效于上面的示例): spring-doc.cadn.net.cn

@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注册。最重要的是,在类声明中通过泛型 类型信息在运行时可用。spring-doc.cadn.net.cn

@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()));
	}

}

你像这样注册它:spring-doc.cadn.net.cn

@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-doc.cadn.net.cn

功能可视化和控制

Spring Cloud Function支持通过Actuator端点以及程序化方式查看在FunctionCatalog中可用的功能。spring-doc.cadn.net.cn

通过编程方式

要通过编程方式查看应用程序上下文中可用的功能,您只需要访问FunctionCatalog。 在那里,您可以找到查找大小、查找函数以及列出所有可用函数的方法。spring-doc.cadn.net.cn

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 框架添加依赖项:spring-doc.cadn.net.cn

<dependency>
     <groupId>org.springframework.boot</groupId>
     <artifactId>spring-boot-starter-web</artifactId>
</dependency>

以下示例显示如何添加 WebFlux 框架的依赖项:spring-doc.cadn.net.cn

<dependency>
       <groupId>org.springframework.boot</groupId>
       <artifactId>spring-boot-starter-webflux</artifactId>
</dependency>

您可以按如下方式添加Actuator依赖项:spring-doc.cadn.net.cn

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

还必须通过设置以下属性来启用functions执行器端点: --management.endpoints.web.exposure.include=functionsspring-doc.cadn.net.cn

访问以下网址查看FunctionCatalog中的功能:
<host>:<port>/actuator/functionsspring-doc.cadn.net.cn

curl http://localhost:8080/actuator/functions

你的输出应类似如下:spring-doc.cadn.net.cn

{"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 用户来说尤为熟悉。spring-doc.cadn.net.cn

假设这是您的应用:spring-doc.cadn.net.cn

@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服务器的集成测试:spring-doc.cadn.net.cn

@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 定义风格时:spring-doc.cadn.net.cn

@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.spring-doc.cadn.net.cn

And to help with correct dependencies here is the excerpt from POMspring-doc.cadn.net.cn

    <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</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应用程序。例如:spring-doc.cadn.net.cn

@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");
	}

}