Improve messaging gateway mapping

The `#args` and `#gatewayMethod` SpEL variables have been deprecated for a while

* Remove their population and usage in favor of `MethodArgsHolder` `root` of the evaluation context
This change optimize a gateway mapping logic the way that there is no need in evaluation context
for every call: we can just reuse a global one
* Some other `GatewayMethodInboundMessageMapper` code style refactoring
* Fix effected test classes and their configs
This commit is contained in:
Artem Bilan
2022-10-06 14:26:38 -04:00
committed by Gary Russell
parent a9f511c170
commit a30dc10447
13 changed files with 105 additions and 122 deletions

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,7 +38,6 @@ import org.springframework.core.annotation.SynthesizingMethodParameter;
import org.springframework.expression.EvaluationContext;
import org.springframework.expression.Expression;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.integration.expression.ExpressionUtils;
import org.springframework.integration.mapping.InboundMessageMapper;
import org.springframework.integration.mapping.MessageMappingException;
@@ -107,9 +106,7 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
private Expression payloadExpression;
private EvaluationContext payloadExpressionEvaluationContext;
private BeanFactory beanFactory;
private EvaluationContext evaluationContext;
@Nullable
private Expression sendTimeoutExpression;
@@ -148,18 +145,14 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
this.globalHeaderExpressions = globalHeaderExpressions;
this.parameterList = getMethodParameterList(method);
this.payloadExpression = parsePayloadExpression(method);
if (messageBuilderFactory == null) {
this.messageBuilderFactory = new DefaultMessageBuilderFactory();
}
else {
this.messageBuilderFactory = messageBuilderFactory;
}
if (mapper == null) {
this.argsMapper = new DefaultMethodArgsMessageMapper();
}
else {
this.argsMapper = mapper;
}
this.messageBuilderFactory =
messageBuilderFactory == null
? new DefaultMessageBuilderFactory()
: messageBuilderFactory;
this.argsMapper =
mapper == null
? new DefaultMethodArgsMessageMapper()
: mapper;
}
@@ -169,8 +162,7 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
this.payloadExpressionEvaluationContext = ExpressionUtils.createStandardEvaluationContext(beanFactory);
this.evaluationContext = ExpressionUtils.createStandardEvaluationContext(beanFactory);
}
public void setSendTimeoutExpression(Expression sendTimeoutExpression) {
@@ -197,38 +189,31 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
try {
return this.argsMapper.toMessage(new MethodArgsHolder(this.method, arguments), headers);
}
catch (MessagingException e) { // NOSONAR to avoid if..else
throw e;
catch (MessagingException ex) { // NOSONAR to avoid if..else
throw ex;
}
catch (Exception e) {
throw new MessageMappingException("Failed to map arguments: " + Arrays.toString(arguments), e);
catch (Exception ex) {
throw new MessageMappingException("Failed to map arguments: " + Arrays.toString(arguments), ex);
}
}
private Map<String, Object> evaluateHeaders(EvaluationContext methodInvocationEvaluationContext,
MethodArgsHolder methodArgsHolder, Map<String, Expression> headerExpressions) {
private Map<String, Object> evaluateHeaders(MethodArgsHolder methodArgsHolder,
Map<String, Expression> headerExpressions) {
Map<String, Object> evaluatedHeaders = new HashMap<>();
for (Map.Entry<String, Expression> entry : headerExpressions.entrySet()) {
Object value = entry.getValue().getValue(methodInvocationEvaluationContext, methodArgsHolder);
Object value = entry.getValue()
.getValue(GatewayMethodInboundMessageMapper.this.evaluationContext, methodArgsHolder);
evaluatedHeaders.put(entry.getKey(), value);
}
return evaluatedHeaders;
}
// TODO Remove in the future release. The MethodArgsHolder as a root object covers this use-case.
private StandardEvaluationContext createMethodInvocationEvaluationContext(Object[] arguments) {
StandardEvaluationContext context = ExpressionUtils.createStandardEvaluationContext(this.beanFactory);
context.setVariable("args", arguments);
context.setVariable("gatewayMethod", this.method);
return context;
}
@Nullable
private Object evaluatePayloadExpression(String expressionString, Object argumentValue) {
Expression expression =
this.parameterPayloadExpressions.computeIfAbsent(expressionString, PARSER::parseExpression);
return expression.getValue(this.payloadExpressionEvaluationContext, argumentValue);
return expression.getValue(this.evaluationContext, argumentValue);
}
@@ -287,15 +272,11 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
public class DefaultMethodArgsMessageMapper implements MethodArgsMessageMapper {
private final MessageBuilderFactory msgBuilderFactory =
GatewayMethodInboundMessageMapper.this.messageBuilderFactory;
@Override
public Message<?> toMessage(MethodArgsHolder holder, @Nullable Map<String, Object> headersToMap) {
Object messageOrPayload = null;
boolean foundPayloadAnnotation = false;
Object[] arguments = holder.getArgs();
EvaluationContext methodInvocationEvaluationContext = createMethodInvocationEvaluationContext(arguments);
Map<String, Object> headersToPopulate =
headersToMap != null
? new HashMap<>(headersToMap)
@@ -303,7 +284,7 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
if (GatewayMethodInboundMessageMapper.this.payloadExpression != null) {
messageOrPayload =
GatewayMethodInboundMessageMapper.this.payloadExpression.getValue(
methodInvocationEvaluationContext, holder);
GatewayMethodInboundMessageMapper.this.evaluationContext, holder);
}
for (int i = 0; i < GatewayMethodInboundMessageMapper.this.parameterList.size(); i++) {
Object argumentValue = arguments[i];
@@ -337,8 +318,8 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
() -> "The 'payload' (or 'Message') for gateway [" + GatewayMethodInboundMessageMapper.this.method +
"] method call cannot be determined (must not be 'null') from the provided arguments: " +
Arrays.toString(arguments));
populateSendAndReplyTimeoutHeaders(methodInvocationEvaluationContext, holder, headersToPopulate);
return buildMessage(holder, headersToPopulate, messageOrPayload, methodInvocationEvaluationContext);
populateSendAndReplyTimeoutHeaders(holder, headersToPopulate);
return buildMessage(holder, headersToPopulate, messageOrPayload);
}
private void headerOrHeaders(Map<String, Object> headersToPopulate, Object argumentValue,
@@ -406,45 +387,46 @@ class GatewayMethodInboundMessageMapper implements InboundMessageMapper<Object[]
copyHeaders(argumentValue, headersToPopulate);
}
private void populateSendAndReplyTimeoutHeaders(EvaluationContext methodInvocationEvaluationContext,
MethodArgsHolder methodArgsHolder, Map<String, Object> headersToPopulate) {
private void populateSendAndReplyTimeoutHeaders(MethodArgsHolder methodArgsHolder,
Map<String, Object> headersToPopulate) {
if (GatewayMethodInboundMessageMapper.this.sendTimeoutExpression != null) {
headersToPopulate.computeIfAbsent(GenericMessagingTemplate.DEFAULT_SEND_TIMEOUT_HEADER,
v -> GatewayMethodInboundMessageMapper.this.sendTimeoutExpression
.getValue(methodInvocationEvaluationContext, methodArgsHolder, Long.class));
.getValue(GatewayMethodInboundMessageMapper.this.evaluationContext,
methodArgsHolder, Long.class));
}
if (GatewayMethodInboundMessageMapper.this.replyTimeoutExpression != null) {
headersToPopulate.computeIfAbsent(GenericMessagingTemplate.DEFAULT_RECEIVE_TIMEOUT_HEADER,
v -> GatewayMethodInboundMessageMapper.this.replyTimeoutExpression
.getValue(methodInvocationEvaluationContext, methodArgsHolder, Long.class));
.getValue(GatewayMethodInboundMessageMapper.this.evaluationContext,
methodArgsHolder, Long.class));
}
}
private Message<?> buildMessage(MethodArgsHolder methodArgsHolder, Map<String, Object> headers,
Object messageOrPayload, EvaluationContext methodInvocationEvaluationContext) {
Object messageOrPayload) {
MessageBuilderFactory msgBuilderFactory = GatewayMethodInboundMessageMapper.this.messageBuilderFactory;
AbstractIntegrationMessageBuilder<?> builder =
(messageOrPayload instanceof Message)
? this.msgBuilderFactory.fromMessage((Message<?>) messageOrPayload)
: this.msgBuilderFactory.withPayload(messageOrPayload);
(messageOrPayload instanceof Message<?> msg)
? msgBuilderFactory.fromMessage(msg)
: msgBuilderFactory.withPayload(messageOrPayload);
builder.copyHeadersIfAbsent(headers);
// Explicit headers in XML override any @Header annotations...
if (!CollectionUtils.isEmpty(GatewayMethodInboundMessageMapper.this.headerExpressions)) {
Map<String, Object> evaluatedHeaders = evaluateHeaders(methodInvocationEvaluationContext,
methodArgsHolder, GatewayMethodInboundMessageMapper.this.headerExpressions);
Map<String, Object> evaluatedHeaders =
evaluateHeaders(methodArgsHolder, GatewayMethodInboundMessageMapper.this.headerExpressions);
builder.copyHeaders(evaluatedHeaders);
}
// ...whereas global (default) headers do not...
if (!CollectionUtils.isEmpty(GatewayMethodInboundMessageMapper.this.globalHeaderExpressions)) {
Map<String, Object> evaluatedHeaders = evaluateHeaders(methodInvocationEvaluationContext,
methodArgsHolder, GatewayMethodInboundMessageMapper.this.globalHeaderExpressions);
Map<String, Object> evaluatedHeaders =
evaluateHeaders(methodArgsHolder,
GatewayMethodInboundMessageMapper.this.globalHeaderExpressions);
builder.copyHeadersIfAbsent(evaluatedHeaders);
}
if (GatewayMethodInboundMessageMapper.this.headers != null) {
builder.copyHeadersIfAbsent(GatewayMethodInboundMessageMapper.this.headers);
}
return builder.build();
return builder.copyHeadersIfAbsent(GatewayMethodInboundMessageMapper.this.headers).build();
}
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2015-2020 the original author or authors.
* Copyright 2015-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.

View File

@@ -1467,7 +1467,7 @@ public class EnableIntegrationTests {
@TestMessagingGateway
public interface TestGateway {
@Gateway(headers = @GatewayHeader(name = "calledMethod", expression = "#gatewayMethod.name"))
@Gateway(headers = @GatewayHeader(name = "calledMethod", expression = "method.name"))
String echo(String payload);
@Gateway(requestChannel = "sendAsyncChannel")
@@ -1482,7 +1482,7 @@ public class EnableIntegrationTests {
@TestMessagingGateway2
public interface TestGateway2 {
@Gateway(headers = @GatewayHeader(name = "calledMethod", expression = "#gatewayMethod.name"))
@Gateway(headers = @GatewayHeader(name = "calledMethod", expression = "method.name"))
String echo2(String payload);
}

View File

@@ -393,10 +393,10 @@ public class AsyncGatewayTests {
CompletableFuture<Message<?>> returnMessageListenable(String s);
@Gateway(headers = @GatewayHeader(name = "method", expression = "#gatewayMethod.name"))
@Gateway(headers = @GatewayHeader(name = "method", expression = "method.name"))
CustomFuture returnCustomFuture(String s);
@Gateway(headers = @GatewayHeader(name = "method", expression = "#gatewayMethod.name"))
@Gateway(headers = @GatewayHeader(name = "method", expression = "method.name"))
Future<?> returnCustomFutureWithTypeFuture(String s);
Mono<String> returnStringPromise(String s);

View File

@@ -13,9 +13,9 @@
service-interface="org.springframework.integration.gateway.GatewayInterfaceTests$Bar"
default-request-channel="requestChannelBaz"
error-channel="errorChannel">
<int:default-header name="name" expression="#gatewayMethod.name"/>
<int:default-header name="string" expression="#gatewayMethod.toString()"/>
<int:default-header name="object" expression="#gatewayMethod"/>
<int:default-header name="name" expression="method.name"/>
<int:default-header name="string" expression="method.toString()"/>
<int:default-header name="object" expression="method"/>
<int:method name="baz">
<int:header name="name" value="overrideGlobal"/>
</int:method>

View File

@@ -522,8 +522,8 @@ public class GatewayInterfaceTests {
void baz(String payload);
@Gateway(payloadExpression = "#args[0]", requestChannel = "lateReplyChannel",
requestTimeoutExpression = "#args[1]", replyTimeoutExpression = "#args[2]")
@Gateway(payloadExpression = "args[0]", requestChannel = "lateReplyChannel",
requestTimeoutExpression = "args[1]", replyTimeoutExpression = "args[2]")
String lateReply(String payload, long requestTimeout, long replyTimeout);
}
@@ -651,7 +651,7 @@ public class GatewayInterfaceTests {
@Profile("gatewayTest")
public interface Int2634Gateway {
@Gateway(requestChannel = "gatewayChannel", payloadExpression = "#args[0]")
@Gateway(requestChannel = "gatewayChannel", payloadExpression = "args[0]")
Object test1(Map<Object, ?> map);
@Gateway(requestChannel = "gatewayChannel")

View File

@@ -8,9 +8,9 @@
<int:gateway id="sampleGateway"
service-interface="org.springframework.integration.gateway.GatewayInterfaceTests.Bar"
default-request-channel="requestChannelBaz" default-payload-expression="'foo'">
<int:default-header name="name" expression="#gatewayMethod.name"/>
<int:default-header name="string" expression="#gatewayMethod.toString()"/>
<int:default-header name="object" expression="#gatewayMethod"/>
<int:default-header name="name" expression="method.name"/>
<int:default-header name="string" expression="method.toString()"/>
<int:default-header name="object" expression="method"/>
<int:method name="baz">
<int:header name="name" value="overrideGlobal"/>
</int:method>

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,13 +17,15 @@
package org.springframework.integration.gateway;
import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
import static org.mockito.Mockito.mock;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.expression.Expression;
@@ -40,6 +42,7 @@ import org.springframework.messaging.handler.annotation.Payload;
/**
* @author Mark Fisher
* @author Gary Russell
* @author Artem Bilan
*/
public class GatewayMethodInboundMessageMapperToMessageTests {
@@ -52,20 +55,22 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
assertThat(message.getPayload()).isEqualTo("test");
}
@Test(expected = IllegalArgumentException.class)
@Test
public void toMessageWithTooManyParameters() throws Exception {
Method method = TestService.class.getMethod("sendPayload", String.class);
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
mapper.setBeanFactory(mock(BeanFactory.class));
mapper.toMessage(new Object[] { "test", "oops" });
assertThatIllegalArgumentException()
.isThrownBy(() -> mapper.toMessage(new Object[] { "test", "oops" }));
}
@Test(expected = IllegalArgumentException.class)
@Test
public void toMessageWithEmptyParameterArray() throws Exception {
Method method = TestService.class.getMethod("sendPayload", String.class);
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
mapper.setBeanFactory(mock(BeanFactory.class));
mapper.toMessage(new Object[] {});
assertThatIllegalArgumentException()
.isThrownBy(() -> mapper.toMessage(new Object[] {}));
}
@Test
@@ -79,13 +84,14 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
assertThat(message.getHeaders().get("foo")).isEqualTo("bar");
}
@Test(expected = MessageMappingException.class)
@Test
public void toMessageWithPayloadAndRequiredHeaderButNullValue() throws Exception {
Method method = TestService.class.getMethod(
"sendPayloadAndHeader", String.class, String.class);
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
mapper.setBeanFactory(mock(BeanFactory.class));
mapper.toMessage(new Object[] { "test", null });
assertThatExceptionOfType(MessageMappingException.class)
.isThrownBy(() -> mapper.toMessage(new Object[] { "test", null }));
}
@Test
@@ -116,7 +122,7 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
"sendPayloadAndHeadersMap", String.class, Map.class);
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
mapper.setBeanFactory(mock(BeanFactory.class));
Map<String, Object> headers = new HashMap<String, Object>();
Map<String, Object> headers = new HashMap<>();
headers.put("abc", 123);
headers.put("def", 456);
Message<?> message = mapper.toMessage(new Object[] { "test", headers });
@@ -135,15 +141,16 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
assertThat(message.getPayload()).isEqualTo("test");
}
@Test(expected = MessageMappingException.class)
@Test
public void toMessageWithPayloadAndHeadersMapWithNonStringKey() throws Exception {
Method method = TestService.class.getMethod(
"sendPayloadAndHeadersMap", String.class, Map.class);
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
mapper.setBeanFactory(mock(BeanFactory.class));
Map<Integer, String> headers = new HashMap<Integer, String>();
Map<Integer, String> headers = new HashMap<>();
headers.put(123, "abc");
mapper.toMessage(new Object[] { "test", headers });
assertThatExceptionOfType(MessageMappingException.class)
.isThrownBy(() -> mapper.toMessage(new Object[] { "test", headers }));
}
@Test
@@ -167,13 +174,14 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
assertThat(message.getHeaders().get("foo")).isEqualTo("bar");
}
@Test(expected = MessageMappingException.class)
@Test
public void toMessageWithMessageParameterAndRequiredHeaderButNullValue() throws Exception {
Method method = TestService.class.getMethod("sendMessageAndHeader", Message.class, String.class);
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
mapper.setBeanFactory(mock(BeanFactory.class));
Message<?> inputMessage = MessageBuilder.withPayload("test message").build();
mapper.toMessage(new Object[] { inputMessage, null });
assertThatExceptionOfType(MessageMappingException.class)
.isThrownBy(() -> mapper.toMessage(new Object[] { inputMessage, null }));
}
@Test
@@ -198,26 +206,28 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
assertThat(message.getHeaders().get("foo")).isNull();
}
@Test(expected = MessageMappingException.class)
@Test
public void noArgs() throws Exception {
Method method = TestService.class.getMethod("noArgs", new Class<?>[] {});
Method method = TestService.class.getMethod("noArgs");
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
mapper.setBeanFactory(mock(BeanFactory.class));
mapper.toMessage(new Object[] {});
assertThatExceptionOfType(MessageMappingException.class)
.isThrownBy(() -> mapper.toMessage(new Object[] {}));
}
@Test(expected = MessageMappingException.class)
@Test
public void onlyHeaders() throws Exception {
Method method = TestService.class.getMethod("onlyHeaders", String.class, String.class);
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
mapper.setBeanFactory(mock(BeanFactory.class));
mapper.toMessage(new Object[] { "abc", "def" });
assertThatExceptionOfType(MessageMappingException.class)
.isThrownBy(() -> mapper.toMessage(new Object[] { "abc", "def" }));
}
@Test
public void toMessageWithPayloadAndHeaders() throws Exception {
Method method = TestService.class.getMethod("sendPayload", String.class);
Map<String, Expression> headers = new HashMap<String, Expression>();
Map<String, Expression> headers = new HashMap<>();
headers.put("foo", new LiteralExpression("foo"));
headers.put("bar", new SpelExpressionParser().parseExpression("6 * 7"));
headers.put("baz", new LiteralExpression("hello"));
@@ -232,7 +242,7 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
@Test
public void toMessageWithNonHeaderMapPayloadExpressionA() throws Exception {
Method method = TestService.class.getMethod("sendNonHeadersMap", Map.class);
Map<Integer, Object> map = new HashMap<Integer, Object>();
Map<Integer, Object> map = new HashMap<>();
map.put(1, "One");
map.put(2, "Two");
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
@@ -258,7 +268,7 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
@Test
public void toMessageWithNonHeaderMapPayloadAnnotation() throws Exception {
Method method = TestService.class.getMethod("sendNonHeadersMapWithPayloadAnnotation", Map.class);
Map<Integer, Object> map = new HashMap<Integer, Object>();
Map<Integer, Object> map = new HashMap<>();
map.put(1, "One");
map.put(2, "Two");
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
@@ -270,10 +280,10 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
@Test
public void toMessageWithTwoMapsOneNonHeaderPayloadExpression() throws Exception {
Method method = TestService.class.getMethod("sendNonHeadersMapFirstArgument", Map.class, Map.class);
Map<Integer, Object> mapA = new HashMap<Integer, Object>();
Map<Integer, Object> mapA = new HashMap<>();
mapA.put(1, "One");
mapA.put(2, "Two");
Map<String, Object> mapB = new HashMap<String, Object>();
Map<String, Object> mapB = new HashMap<>();
mapB.put("1", "ONE");
mapB.put("2", "TWO");
GatewayMethodInboundMessageMapper mapper = new GatewayMethodInboundMessageMapper(method);
@@ -310,7 +320,7 @@ public class GatewayMethodInboundMessageMapperToMessageTests {
void sendNonHeadersMap(Map<Integer, Object> map);
@Payload("#args[0]")
@Payload("args[0]")
void sendNonHeadersMapWithPayloadAnnotation(Map<Integer, Object> map);
void sendNonHeadersMapFirstArgument(Map<Integer, Object> mapA, Map<String, Object> mapB);

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -226,10 +226,10 @@ public class GatewayProxyMessageMappingTests {
void twoMapsAndOneAnnotatedWithPayload(@Payload Map<String, Object> payload, Map<String, Object> headers);
@Payload("#args[0] + #args[1] + '!'")
@Payload("args[0] + args[1] + '!'")
void payloadAnnotationAtMethodLevel(String a, String b);
@Payload("@testBean.exclaim(#args[0])")
@Payload("@testBean.exclaim(args[0])")
void payloadAnnotationAtMethodLevelUsingBeanResolver(String s);
void payloadAnnotationWithExpression(@Payload("toUpperCase()") String s);

View File

@@ -8,13 +8,13 @@
https://www.springframework.org/schema/integration/spring-integration.xsd">
<int:gateway id="gateway" service-interface="org.springframework.integration.gateway.GatewayWithPayloadExpressionTests$SampleGateway">
<int:method name="send1" request-channel="input" payload-expression="#args[0] + 'bar'"/>
<int:method name="send2" request-channel="input" payload-expression="@testBean.sum(#args[0])"/>
<int:method name="send3" request-channel="input" payload-expression="#gatewayMethod.name"/>
<int:method name="send1" request-channel="input" payload-expression="args[0] + 'bar'"/>
<int:method name="send2" request-channel="input" payload-expression="@testBean.sum(args[0])"/>
<int:method name="send3" request-channel="input" payload-expression="method.name"/>
</int:gateway>
<int:gateway id="annotatedGateway"
service-interface="org.springframework.integration.gateway.GatewayWithPayloadExpressionTests.SampleAnnotatedGateway"
service-interface="org.springframework.integration.gateway.GatewayWithPayloadExpressionTests$SampleAnnotatedGateway"
default-request-channel="input"/>
<int:channel id="input">

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2019 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,15 +18,13 @@ package org.springframework.integration.gateway;
import static org.assertj.core.api.Assertions.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.PollableChannel;
import org.springframework.messaging.handler.annotation.Payload;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
/**
* @author Mark Fisher
@@ -35,8 +33,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
*
* @since 2.0
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@SpringJUnitConfig
public class GatewayWithPayloadExpressionTests {
@Autowired
@@ -91,7 +88,7 @@ public class GatewayWithPayloadExpressionTests {
public interface SampleAnnotatedGateway {
@Payload("#args[0] + #args[1]")
@Payload("args[0] + args[1]")
void send(String value1, String value2);
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2021 the original author or authors.
* Copyright 2002-2022 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -38,7 +38,7 @@ public interface TestService {
void oneWay(String input);
@Payload("#args[0]")
@Payload("args[0]")
void oneWayWithTimeouts(String input, Long sendTimeout, Long receiveTimeout);
String solicitResponse();
@@ -51,7 +51,7 @@ public interface TestService {
Message<?> requestReplyWithMessageReturnValue(String input);
@Payload("#gatewayMethod.name + #args.length")
@Payload("method.name + args.length")
String requestReplyWithPayloadAnnotation();
Future<Message<?>> async(String s);

View File

@@ -163,13 +163,7 @@ NOTE: If a no-argument gateway is specified in XML, and the interface method has
The `<header/>` element supports `expression` as an alternative to `value`.
The SpEL expression is evaluated to determine the value of the header.
Starting with version 5.2, the `#root` object of the evaluation context is a `MethodArgsHolder` with `getMethod()` and `getArgs()` accessors.
These two expression evaluation context variables are deprecated since version 5.2:
* #args: An `Object[]` containing the method arguments
* #gatewayMethod: The object (derived from `java.reflect.Method`) that represents the method in the `service-interface` that was invoked.
A header containing this variable can be used later in the flow (for example, for routing).
For example, if you wish to route on the simple method name, you might add a header with the following expression: `#gatewayMethod.name`.
For example, if you wish to route on the simple method name, you might add a header with the following expression: `method.name`.
NOTE: The `java.reflect.Method` is not serializable.
A header with an expression of `method` is lost if you later serialize the message.