or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

configuration.mdinbound-endpoints.mdindex.mdjava-dsl.mdmanagement.mdmessage-conversion.mdmultipart-handling.mdoutbound-handlers.md
tile.json

outbound-handlers.mddocs/

HTTP Outbound Handlers

Components for executing HTTP requests from Spring Integration messages. Outbound handlers use Spring's RestTemplate to make HTTP calls, supporting both one-way (fire-and-forget) and request-reply patterns with extensive configuration options for URIs, HTTP methods, headers, and response handling.

Key Information for Agents

Required Dependencies:

  • spring-web (for RestTemplate)
  • spring-integration-core is required
  • No servlet container required (works in any Spring context)

Default Behaviors:

  • expectReply=true by default (request-reply pattern)
  • Default HTTP method: POST (if not specified)
  • Default RestTemplate created if not provided
  • extractPayload=true by default (only payload sent as request body)
  • extractResponseBody=true by default (only body in reply, not full ResponseEntity)
  • transferCookies=false by default
  • No timeout by default (uses RestTemplate/HTTP client defaults)
  • Default message converters: JSON, XML, String, Form (via RestTemplate defaults)

Threading Model:

  • Executes on the thread that processes the message
  • Blocking I/O by default (RestTemplate is synchronous)
  • For async processing, use async channels (ExecutorChannel, QueueChannel)
  • Timeouts configured via ClientHttpRequestFactory (connect timeout, read timeout)

URI Configuration:

  • Can be static String, URI object, or SpEL Expression
  • Expression evaluated against each message
  • URI template variables supported: {variableName}
  • Variables provided via setUriVariableExpressions() or setUriVariablesExpression()

Response Handling:

  • expectedResponseType required when expectReply=true
  • Supports Class<?>, ParameterizedTypeReference<?>, or SpEL Expression
  • If extractResponseBody=false, returns full ResponseEntity<?>
  • If extractResponseBody=true, returns only response body

Exceptions:

  • HttpClientErrorException - 4xx HTTP errors (wrapped in MessagingException)
  • HttpServerErrorException - 5xx HTTP errors (wrapped in MessagingException)
  • HttpMessageNotReadableException - response conversion failures
  • HttpMessageNotWritableException - request conversion failures
  • MessageTimeoutException - if reply timeout exceeded (when expectReply=true)

Edge Cases:

  • If expectReply=false, handler returns null immediately after sending request
  • If URI expression evaluates to null, throws IllegalArgumentException
  • If HTTP method expression evaluates to null, throws IllegalArgumentException
  • If response type expression evaluates to null, uses default conversion
  • URI variables not provided result in template not being resolved (exception thrown)
  • Empty response body with extractResponseBody=true returns null
  • Cookie transfer only works if same RestTemplate instance used for multiple requests

Performance Considerations:

  • Connection pooling recommended for high-throughput scenarios
  • Configure timeouts to prevent hanging requests
  • Use async channels for non-blocking processing
  • Consider connection pool size based on concurrent request volume

Capabilities

HttpRequestExecutingMessageHandler

The primary message handler for executing HTTP requests using RestTemplate. Processes messages from Spring Integration flows and makes HTTP calls to external services, with optional response handling for request-reply scenarios.

public class HttpRequestExecutingMessageHandler
    extends AbstractHttpRequestExecutingMessageHandler {

    /**
     * Creates handler for static URI.
     *
     * @param uri the target URI
     */
    public HttpRequestExecutingMessageHandler(URI uri);

    /**
     * Creates handler for URI string.
     *
     * @param uri the target URI as string
     */
    public HttpRequestExecutingMessageHandler(String uri);

    /**
     * Creates handler with SpEL expression for dynamic URI evaluation.
     * Expression is evaluated against each message.
     *
     * @param uriExpression the URI expression
     */
    public HttpRequestExecutingMessageHandler(Expression uriExpression);

    /**
     * Creates handler with URI and custom RestTemplate.
     *
     * @param uri the target URI as string
     * @param restTemplate the RestTemplate to use
     */
    public HttpRequestExecutingMessageHandler(
        String uri,
        RestTemplate restTemplate);

    /**
     * Creates handler with URI expression and custom RestTemplate.
     *
     * @param uriExpression the URI expression
     * @param restTemplate the RestTemplate to use
     */
    public HttpRequestExecutingMessageHandler(
        Expression uriExpression,
        RestTemplate restTemplate);

    /**
     * Sets ClientHttpRequestFactory for customizing HTTP client behavior.
     * Allows configuration of timeouts, connection pooling, etc.
     *
     * @param requestFactory the request factory
     */
    public void setRequestFactory(ClientHttpRequestFactory requestFactory);

    /**
     * Sets ResponseErrorHandler for custom HTTP error handling.
     * Default handler throws exceptions for 4xx and 5xx responses.
     *
     * @param errorHandler the error handler
     */
    public void setErrorHandler(ResponseErrorHandler errorHandler);

    /**
     * Sets HttpMessageConverters for request/response conversion.
     * Replaces default converters provided by RestTemplate.
     *
     * @param messageConverters the list of message converters
     */
    public void setMessageConverters(
        List<HttpMessageConverter<?>> messageConverters);

    /**
     * Sets encoding mode for URI template processing.
     *
     * @param encodingMode the encoding mode
     */
    public void setEncodingMode(
        DefaultUriBuilderFactory.EncodingMode encodingMode);

    public String getComponentType();
}

Usage Example - Basic GET Request:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.integration.http.outbound.HttpRequestExecutingMessageHandler;
import org.springframework.http.HttpMethod;

@Configuration
public class OutboundGetConfig {

    @Bean
    public HttpRequestExecutingMessageHandler getDataHandler() {
        HttpRequestExecutingMessageHandler handler =
            new HttpRequestExecutingMessageHandler(
                "https://api.example.com/data/{id}");

        handler.setHttpMethod(HttpMethod.GET);
        handler.setExpectedResponseType(DataResponse.class);

        // Configure URI variables
        SpelExpressionParser parser = new SpelExpressionParser();
        Map<String, Expression> uriVars = new HashMap<>();
        uriVars.put("id", parser.parseExpression("payload.dataId"));
        handler.setUriVariableExpressions(uriVars);

        return handler;
    }
}

Usage Example - POST Request with JSON:

@Bean
public HttpRequestExecutingMessageHandler postOrderHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(
            "https://api.example.com/orders");

    handler.setHttpMethod(HttpMethod.POST);
    handler.setExpectedResponseType(OrderResponse.class);

    // Extract payload for request body
    handler.setExtractPayload(true);

    // Configure custom request factory with timeouts
    SimpleClientHttpRequestFactory factory =
        new SimpleClientHttpRequestFactory();
    factory.setConnectTimeout(5000);
    factory.setReadTimeout(10000);
    handler.setRequestFactory(factory);

    return handler;
}

Usage Example - Dynamic URI and Method:

@Bean
public HttpRequestExecutingMessageHandler dynamicHandler() {
    SpelExpressionParser parser = new SpelExpressionParser();

    // Dynamic URI based on message headers
    Expression uriExpression = parser.parseExpression(
        "headers['target_url']");

    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(uriExpression);

    // Dynamic HTTP method
    handler.setHttpMethodExpression(
        parser.parseExpression("headers['http_method']"));

    // Dynamic response type
    handler.setExpectedResponseTypeExpression(
        parser.parseExpression("headers['response_type']"));

    handler.setExpectReply(true);

    return handler;
}

AbstractHttpRequestExecutingMessageHandler

Base class for HTTP outbound handlers providing common configuration and functionality. This abstract class can be extended to create custom HTTP request executors with specialized behavior.

public abstract class AbstractHttpRequestExecutingMessageHandler
    extends AbstractReplyProducingMessageHandler {

    /**
     * Creates handler with URI expression.
     *
     * @param uriExpression the URI expression
     */
    protected AbstractHttpRequestExecutingMessageHandler(
        Expression uriExpression);

    /**
     * Sets URI encoding mode for template variable encoding.
     * Default: TEMPLATE_AND_VALUES.
     *
     * @param encodingMode the encoding mode
     */
    public void setEncodingMode(
        DefaultUriBuilderFactory.EncodingMode encodingMode);

    /**
     * Sets SpEL expression to determine HttpMethod dynamically.
     * Expression is evaluated against each message.
     *
     * @param httpMethodExpression the HTTP method expression
     */
    public void setHttpMethodExpression(Expression httpMethodExpression);

    /**
     * Sets the HttpMethod for all requests.
     * Default: POST.
     *
     * @param httpMethod the HTTP method
     */
    public void setHttpMethod(HttpMethod httpMethod);

    /**
     * Controls whether message payload should be extracted for request body.
     * When false, entire Message is used (rarely needed).
     * Default: true.
     *
     * @param extractPayload true to extract payload
     */
    public void setExtractPayload(boolean extractPayload);

    /**
     * Sets charset for converting String payloads to bytes.
     * Default: UTF-8.
     *
     * @param charset the charset name
     */
    public void setCharset(String charset);

    /**
     * Returns whether reply Message is expected.
     *
     * @return true if reply is expected
     */
    public boolean isExpectReply();

    /**
     * Sets whether reply Message is expected (request-reply vs fire-and-forget).
     * Default: true.
     *
     * @param expectReply true for request-reply
     */
    public void setExpectReply(boolean expectReply);

    /**
     * Sets expected response type for conversion.
     *
     * @param expectedResponseType the response type class
     */
    public void setExpectedResponseType(Class<?> expectedResponseType);

    /**
     * Sets SpEL expression to determine response type dynamically.
     * Expression is evaluated against each message.
     *
     * @param expectedResponseTypeExpression the response type expression
     */
    public void setExpectedResponseTypeExpression(
        Expression expectedResponseTypeExpression);

    /**
     * Sets HeaderMapper for mapping between Message and HTTP headers.
     *
     * @param headerMapper the header mapper
     */
    public void setHeaderMapper(HeaderMapper<HttpHeaders> headerMapper);

    /**
     * Sets Map of SpEL expressions for URI variable substitution.
     * Each expression is evaluated against the message.
     *
     * @param uriVariableExpressions map of variable name to expression
     */
    public void setUriVariableExpressions(
        Map<String, Expression> uriVariableExpressions);

    /**
     * Sets SpEL expression to evaluate a Map of URI variables.
     * Expression should return Map<String, ?>.
     *
     * @param uriVariablesExpression the URI variables expression
     */
    public void setUriVariablesExpression(Expression uriVariablesExpression);

    /**
     * Controls whether Set-Cookie headers from responses should be
     * transferred as Cookie headers in subsequent requests.
     * Useful for maintaining sessions across requests.
     * Default: false.
     *
     * @param transferCookies true to transfer cookies
     */
    public void setTransferCookies(boolean transferCookies);

    /**
     * Sets whether to trust SpEL expressions from message headers.
     * When false, expressions are not evaluated (security).
     * Default: false.
     *
     * @param trustedSpel true to trust SpEL
     */
    public void setTrustedSpel(boolean trustedSpel);

    /**
     * Controls whether ResponseEntity body should be extracted for reply.
     * When false, entire ResponseEntity (with status, headers) is returned.
     * Default: true.
     *
     * @param extractResponseBody true to extract body only
     */
    public void setExtractResponseBody(boolean extractResponseBody);

    public IntegrationPatternType getIntegrationPatternType();

    /**
     * Abstract method to perform HTTP exchange.
     * Must be implemented by subclasses.
     *
     * @param uri the target URI (String or URI)
     * @param httpMethod the HTTP method
     * @param httpRequest the HTTP request entity
     * @param expectedResponseType the expected response type
     * @param requestMessage the original message
     * @param uriVariables the URI variables map
     * @return the response entity
     */
    protected abstract Object exchange(
        Object uri,
        HttpMethod httpMethod,
        HttpEntity<?> httpRequest,
        Object expectedResponseType,
        Message<?> requestMessage,
        Map<String, ?> uriVariables);
}

Advanced Configuration

Custom RestTemplate Configuration

Configure RestTemplate with custom settings for connection pooling, timeouts, and SSL:

import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;

@Bean
public HttpRequestExecutingMessageHandler customRestTemplateHandler() {
    // Configure connection pooling
    PoolingHttpClientConnectionManager connectionManager =
        new PoolingHttpClientConnectionManager();
    connectionManager.setMaxTotal(100);
    connectionManager.setDefaultMaxPerRoute(20);

    // Build HTTP client
    CloseableHttpClient httpClient = HttpClients.custom()
        .setConnectionManager(connectionManager)
        .build();

    // Create request factory with timeouts
    HttpComponentsClientHttpRequestFactory factory =
        new HttpComponentsClientHttpRequestFactory(httpClient);
    factory.setConnectTimeout(5000);
    factory.setReadTimeout(10000);

    // Create RestTemplate
    RestTemplate restTemplate = new RestTemplate(factory);

    // Configure message converters
    List<HttpMessageConverter<?>> converters = new ArrayList<>();
    converters.add(new MappingJackson2HttpMessageConverter());
    converters.add(new StringHttpMessageConverter());
    restTemplate.setMessageConverters(converters);

    // Create handler
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(
            "https://api.example.com/data",
            restTemplate);

    handler.setHttpMethod(HttpMethod.POST);
    handler.setExpectedResponseType(String.class);

    return handler;
}

Custom Error Handling

Implement custom error handling for HTTP responses:

import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.ResponseErrorHandler;

@Bean
public HttpRequestExecutingMessageHandler errorHandlingHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(
            "https://api.example.com/data");

    // Custom error handler
    handler.setErrorHandler(new ResponseErrorHandler() {
        @Override
        public boolean hasError(ClientHttpResponse response)
                throws IOException {
            int statusCode = response.getStatusCode().value();
            return statusCode >= 400;
        }

        @Override
        public void handleError(ClientHttpResponse response)
                throws IOException {
            int statusCode = response.getStatusCode().value();
            String statusText = response.getStatusText();

            if (statusCode >= 500) {
                throw new ServerErrorException(
                    "Server error: " + statusCode + " " + statusText);
            } else if (statusCode >= 400) {
                throw new ClientErrorException(
                    "Client error: " + statusCode + " " + statusText);
            }
        }
    });

    handler.setHttpMethod(HttpMethod.GET);
    handler.setExpectedResponseType(String.class);

    return handler;
}

URI Variable Substitution

Configure complex URI variable substitution with SpEL expressions:

@Bean
public HttpRequestExecutingMessageHandler uriVariableHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(
            "https://api.example.com/{version}/users/{userId}/orders/{orderId}");

    SpelExpressionParser parser = new SpelExpressionParser();
    Map<String, Expression> uriVars = new HashMap<>();

    // Extract from payload
    uriVars.put("userId", parser.parseExpression("payload.userId"));
    uriVars.put("orderId", parser.parseExpression("payload.orderId"));

    // Extract from headers
    uriVars.put("version", parser.parseExpression("headers['api-version']"));

    handler.setUriVariableExpressions(uriVars);
    handler.setHttpMethod(HttpMethod.GET);
    handler.setExpectedResponseType(Order.class);

    return handler;
}

Alternative - URI Variables from Expression

Use a single expression to provide all URI variables:

@Bean
public HttpRequestExecutingMessageHandler uriVarsExpressionHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(
            "https://api.example.com/{type}/{id}");

    SpelExpressionParser parser = new SpelExpressionParser();

    // Expression returns Map<String, Object>
    handler.setUriVariablesExpression(
        parser.parseExpression("payload.uriParameters"));

    handler.setHttpMethod(HttpMethod.GET);
    handler.setExpectedResponseType(String.class);

    return handler;
}

Header Mapping Configuration

Configure which headers are mapped from messages to HTTP requests:

import org.springframework.integration.http.support.DefaultHttpHeaderMapper;

@Bean
public HttpRequestExecutingMessageHandler headerMappingHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(
            "https://api.example.com/data");

    // Configure header mapper
    DefaultHttpHeaderMapper headerMapper =
        DefaultHttpHeaderMapper.outboundMapper();

    // Map specific headers
    headerMapper.setOutboundHeaderNames(
        "Content-Type",
        "Accept",
        "Authorization",
        "X-Custom-*");  // Pattern for custom headers

    // Set prefix for user-defined headers
    headerMapper.setUserDefinedHeaderPrefix("X-");

    handler.setHeaderMapper(headerMapper);
    handler.setHttpMethod(HttpMethod.POST);
    handler.setExpectedResponseType(String.class);

    return handler;
}

Cookie Transfer for Session Management

Enable cookie transfer to maintain sessions across requests:

@Bean
public HttpRequestExecutingMessageHandler sessionHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(
            "https://api.example.com/data");

    // Enable cookie transfer
    handler.setTransferCookies(true);

    handler.setHttpMethod(HttpMethod.GET);
    handler.setExpectedResponseType(String.class);

    return handler;
}

When transferCookies is enabled, Set-Cookie headers from responses are automatically converted to Cookie headers in subsequent requests, maintaining session state.

Full ResponseEntity Access

Access complete response including status code and headers:

@Bean
public HttpRequestExecutingMessageHandler fullResponseHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(
            "https://api.example.com/data");

    handler.setHttpMethod(HttpMethod.GET);

    // Don't extract body - return full ResponseEntity
    handler.setExtractResponseBody(false);

    // Response type is ResponseEntity<String>
    handler.setExpectedResponseType(String.class);

    return handler;
}

The reply message will contain a ResponseEntity<String> as payload, allowing access to status code, headers, and body.

Fire-and-Forget Pattern

Configure one-way communication without expecting responses:

@Bean
public HttpRequestExecutingMessageHandler fireAndForgetHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(
            "https://api.example.com/events");

    handler.setHttpMethod(HttpMethod.POST);

    // Don't expect reply
    handler.setExpectReply(false);

    // No response type needed
    handler.setExtractPayload(true);

    return handler;
}

When expectReply is false, the handler doesn't wait for or process the response, returning null immediately after sending the request.

Dynamic Response Type Resolution

Determine response type dynamically based on message content:

@Bean
public HttpRequestExecutingMessageHandler dynamicResponseTypeHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(
            "https://api.example.com/data");

    SpelExpressionParser parser = new SpelExpressionParser();

    // Dynamic response type based on message header
    handler.setExpectedResponseTypeExpression(
        parser.parseExpression(
            "headers['response_format'] == 'json' ? " +
            "T(com.example.JsonResponse) : T(com.example.XmlResponse)"));

    handler.setHttpMethod(HttpMethod.GET);
    handler.setExpectReply(true);

    return handler;
}

Parameterized Type Response

Handle generic response types with ParameterizedTypeReference:

import org.springframework.core.ParameterizedTypeReference;

@Bean
public HttpRequestExecutingMessageHandler genericResponseHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(
            "https://api.example.com/users");

    handler.setHttpMethod(HttpMethod.GET);

    // For List<User> response
    ParameterizedTypeReference<List<User>> responseType =
        new ParameterizedTypeReference<List<User>>() {};

    handler.setExpectedResponseType(responseType);

    return handler;
}

URI Encoding Configuration

Configure how URI templates are encoded:

import org.springframework.web.util.DefaultUriBuilderFactory;

@Bean
public HttpRequestExecutingMessageHandler encodingHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(
            "https://api.example.com/search?q={query}");

    // Configure URI encoding mode
    handler.setEncodingMode(
        DefaultUriBuilderFactory.EncodingMode.VALUES_ONLY);

    SpelExpressionParser parser = new SpelExpressionParser();
    Map<String, Expression> uriVars = new HashMap<>();
    uriVars.put("query", parser.parseExpression("payload.searchTerm"));
    handler.setUriVariableExpressions(uriVars);

    handler.setHttpMethod(HttpMethod.GET);
    handler.setExpectedResponseType(String.class);

    return handler;
}

Available encoding modes:

  • TEMPLATE_AND_VALUES: Encode both template and variable values
  • VALUES_ONLY: Encode only variable values (template already encoded)
  • URI_COMPONENT: Encode each URI component separately
  • NONE: No encoding

Charset Configuration

Set charset for String payload conversion:

@Bean
public HttpRequestExecutingMessageHandler charsetHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(
            "https://api.example.com/data");

    handler.setHttpMethod(HttpMethod.POST);

    // Set charset for String to byte[] conversion
    handler.setCharset("ISO-8859-1");

    handler.setExpectedResponseType(String.class);

    return handler;
}

Integration Flow Patterns

Request-Reply Flow

Complete integration flow with HTTP request-reply:

@Bean
public IntegrationFlow httpRequestReplyFlow() {
    return IntegrationFlow
        .from("inputChannel")
        .handle(outboundGatewayHandler())
        .transform(response -> processResponse(response))
        .channel("outputChannel")
        .get();
}

@Bean
public HttpRequestExecutingMessageHandler outboundGatewayHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(
            "https://api.example.com/process");

    handler.setHttpMethod(HttpMethod.POST);
    handler.setExpectReply(true);
    handler.setExpectedResponseType(String.class);

    return handler;
}

Error Handling Flow

Integration flow with HTTP error handling:

@Bean
public IntegrationFlow httpWithErrorHandlingFlow() {
    return IntegrationFlow
        .from("inputChannel")
        .handle(outboundHandler(),
            e -> e.advice(retryAdvice()))
        .transform(response -> processResponse(response))
        .channel("outputChannel")
        .get();
}

@Bean
public RequestHandlerRetryAdvice retryAdvice() {
    RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice();

    RetryTemplate retryTemplate = new RetryTemplate();

    // Retry on HTTP errors
    SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
    retryPolicy.setMaxAttempts(3);
    retryTemplate.setRetryPolicy(retryPolicy);

    // Exponential backoff
    ExponentialBackOffPolicy backOffPolicy =
        new ExponentialBackOffPolicy();
    backOffPolicy.setInitialInterval(1000);
    backOffPolicy.setMultiplier(2.0);
    backOffPolicy.setMaxInterval(10000);
    retryTemplate.setBackOffPolicy(backOffPolicy);

    advice.setRetryTemplate(retryTemplate);

    return advice;
}

Enrichment Pattern

Enrich messages with data from HTTP service:

@Bean
public IntegrationFlow enrichmentFlow() {
    return IntegrationFlow
        .from("orderChannel")
        .enrich(e -> e
            .requestChannel("lookupCustomerChannel")
            .requestPayload(m -> m.getPayload().getCustomerId())
            .propertyExpression("customerData", "payload"))
        .handle("orderService", "processOrder")
        .get();
}

@Bean
public IntegrationFlow customerLookupFlow() {
    return IntegrationFlow
        .from("lookupCustomerChannel")
        .handle(customerLookupHandler())
        .get();
}

@Bean
public HttpRequestExecutingMessageHandler customerLookupHandler() {
    HttpRequestExecutingMessageHandler handler =
        new HttpRequestExecutingMessageHandler(
            "https://api.example.com/customers/{id}");

    SpelExpressionParser parser = new SpelExpressionParser();
    Map<String, Expression> uriVars = new HashMap<>();
    uriVars.put("id", parser.parseExpression("payload"));
    handler.setUriVariableExpressions(uriVars);

    handler.setHttpMethod(HttpMethod.GET);
    handler.setExpectedResponseType(Customer.class);

    return handler;
}