skip to content

How does Spring Cloud Function select and route which function to run, and how do you compose functions?

level: middleimportance: should knowfreq 45%

answer

  1. spring.cloud.function.definition = bean name
  2. pipe | composes functions
  3. FunctionCatalog / FunctionRegistry
  4. RoutingFunction = functionRouter
  5. route via header, routing-expression, or MessageRoutingCallback

basics

~20 s

The 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 s

Spring 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
java
@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

for a junior

Knows the definition property names the function and pipe composes them.

for a middle

Explains FunctionCatalog, static composition vs dynamic RoutingFunction and its three routing inputs.

for a senior

Discusses header propagation across brokers and composition type-conversion pitfalls.

for a principal

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

context