How does Spring Cloud Function select and route which function to run, and how do you compose functions?
answer
- spring.cloud.function.definition = bean name
- pipe | composes functions
- FunctionCatalog / FunctionRegistry
- RoutingFunction = functionRouter
- route via header, routing-expression, or MessageRoutingCallback
basics
~20 sThe active function is chosen by the spring.cloud.function.definition property (the bean name). You compose functions with the pipe symbol, e.g. "uppercase|reverse". For dynamic dispatch, RoutingFunction picks a target per message using a header or expression.
solid answer
~40 sSpring Cloud Function resolves functions through a FunctionCatalog. Which one is active is controlled by the spring.cloud.function.definition property, whose value is the bean name; if multiple functions exist and none is set, resolution is ambiguous. Composition uses the pipe character in that same property — "uppercase|reverse" chains outputs to inputs, and output/input types must be compatible (with transparent conversion). For runtime dispatch, Spring Cloud Function ships a built-in RoutingFunction, activated by setting the definition to functionRouter or enabling routing; the target is chosen per invocation from the spring.cloud.function.definition message header, a routing-expression (SpEL over the Message), or a MessageRoutingCallback bean. This lets one deployed endpoint fan out to many functions — useful in stream and serverless setups where you deploy a single artifact.
code
java · 26 lines@SpringBootApplication
public class RoutingApp {
@Bean
public Function<String, String> uppercase() { return String::toUpperCase; }
@Bean
public Function<String, String> reverse() {
return s -> new StringBuilder(s).reverse().toString();
}
// Programmatic routing: choose target function per message
@Bean
public MessageRoutingCallback router() {
return new MessageRoutingCallback() {
@Override
public String routingResult(Message<?> message) {
return (String) message.getHeaders()
.getOrDefault("op", "uppercase");
}
};
}
// application.properties:
// Static composition: spring.cloud.function.definition=uppercase|reverse
// Dynamic routing: spring.cloud.function.definition=functionRouter
}go deeper
Knows the definition property names the function and pipe composes them.
Explains FunctionCatalog, static composition vs dynamic RoutingFunction and its three routing inputs.
Discusses header propagation across brokers and composition type-conversion pitfalls.
Reasons about single-artifact fan-out designs and when routing beats separate deployments.
**FunctionCatalog** is the central registry. At startup, Spring Cloud Function discovers all `Function`/`Supplier`/`Consumer` beans and registers them in a `FunctionCatalog` (implemented by a `FunctionRegistry`). Adapters look functions up by name from this catalog. The catalog is also what applies **transparent type conversion** and wraps beans as `FunctionInvocationWrapper` so composition and Message handling work uniformly. **Selecting the active function — `spring.cloud.function.definition`:** this property names the bean(s) to expose. With a single function bean it can be inferred, but with several beans you should set it explicitly, e.g. `spring.cloud.function.definition=uppercase`. In Spring Cloud Stream the same property tells the binder which function to bind to the broker. **Composition — the pipe `|`:** you can chain functions declaratively without code: ``` spring.cloud.function.definition=uppercase|reverse ``` The output of `uppercase` feeds the input of `reverse`. Types must line up; Spring inserts converters where possible. A `Supplier|Function` composes into a `Supplier`, and `Function|Consumer` composes into a `Consumer`. This is powerful because you can recombine small, single-purpose beans at deploy time. **Dynamic routing — `RoutingFunction`:** sometimes the target must be chosen per message at runtime. Spring Cloud Function provides a built-in **`RoutingFunction`** (bean name `functionRouter`). Enable it and choose the target by any of: - **Message header** `spring.cloud.function.definition` — the producer stamps which function to run. - **`spring.cloud.function.routing-expression`** — a SpEL expression evaluated against the `Message` (e.g. `headers['type']`). - **A `MessageRoutingCallback` bean** — programmatic routing logic returning the function name. Routing lets you deploy **one** endpoint/handler that internally fans out to many functions — very common in FaaS (a single Lambda) and stream topologies. **Gotchas & edge cases:** - Ambiguity: multiple beans and no `definition` set leads to errors or the wrong function being picked; be explicit. - Composition type mismatch fails if no converter bridges the types. - Header-based routing requires the header to actually survive the transport; brokers may need header mapping configured. - `RoutingFunction` needs one of its inputs (header/expression/callback) present, otherwise it cannot decide and throws. - Bean names with camelCase are used verbatim in the property/header. **When to use routing vs composition:** composition is *static* (fixed pipeline chosen at deploy), routing is *dynamic* (per-message decision). Use composition to assemble a fixed pipeline; use routing when the same deployment must handle heterogeneous message types.
- What happens if you have two function beans and never set spring.cloud.function.definition?Resolution is ambiguous — the adapter cannot reliably pick one. You should set the property (or use the routing function). With a single bean it can be inferred, but with multiple you must be explicit.
saying these in an interview costs you the question
- Claiming composition uses '.' or '->' instead of the pipe '|'
- Thinking RoutingFunction chooses by bean order rather than header/expression/callback
- Believing spring.cloud.function.definition holds a class name rather than the bean name