Skip to content

Commit f396cd3

Browse files
committed
spring-projectsGH-3118: MessagingGW: Don't proxy default methods
Fixes spring-projects#3118 The `GatewayProxyFactoryBean` proxies all the methods in the provided interface, including `default` and `static`. This is not what is expected from end-users. * Proxy only `abstract` methods from the provided interface * Introduce a `DefaultMethodInvokingMethodInterceptor` to call `default` method on the interface using a `MethodHandle` approach for those methods calling
1 parent 5b62cd6 commit f396cd3

File tree

3 files changed

+40
-5
lines changed

3 files changed

+40
-5
lines changed

spring-integration-core/src/main/java/org/springframework/integration/gateway/GatewayProxyFactoryBean.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@
3939
import org.springframework.aop.framework.AdvisedSupport;
4040
import org.springframework.aop.framework.ProxyFactory;
4141
import org.springframework.aop.support.AopUtils;
42-
import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;
4342
import org.springframework.beans.SimpleTypeConverter;
4443
import org.springframework.beans.TypeConverter;
4544
import org.springframework.beans.factory.BeanClassLoaderAware;

spring-integration-core/src/test/java/org/springframework/integration/dsl/gateway/GatewayDslTests.java

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2121

2222
import java.lang.reflect.Method;
23+
import java.util.Map;
24+
import java.util.Objects;
2325
import java.util.function.Function;
2426

2527
import org.junit.jupiter.api.Test;
@@ -35,6 +37,8 @@
3537
import org.springframework.integration.dsl.IntegrationFlow;
3638
import org.springframework.integration.dsl.IntegrationFlows;
3739
import org.springframework.integration.dsl.MessageChannels;
40+
import org.springframework.integration.gateway.GatewayProxyFactoryBean;
41+
import org.springframework.integration.gateway.MessagingGatewaySupport;
3842
import org.springframework.integration.gateway.MethodArgsHolder;
3943
import org.springframework.integration.support.MessageBuilder;
4044
import org.springframework.messaging.Message;
@@ -102,18 +106,28 @@ void testNestedGatewayErrorPropagation() {
102106
}
103107

104108
@Autowired
105-
private Function<Object, Message<?>> functionGateay;
109+
private MessageFunction functionGateway;
110+
111+
@Autowired
112+
@Qualifier("&functionGateway.gateway")
113+
private GatewayProxyFactoryBean functionGatewayFactoryBean;
106114

107115
@Test
108116
void testHeadersFromFunctionGateway() {
109-
Object payload = this.functionGateay
117+
Object payload = this.functionGateway
110118
.andThen(message -> {
111119
assertThat(message.getHeaders()).containsKeys("gatewayMethod", "gatewayArgs");
112120
return message.getPayload();
113121
})
114122
.apply("testPayload");
115123

116124
assertThat(payload).isEqualTo("testPayload");
125+
126+
Map<Method, MessagingGatewaySupport> gateways = this.functionGatewayFactoryBean.getGateways();
127+
assertThat(gateways).hasSize(1);
128+
129+
Method method = gateways.keySet().iterator().next();
130+
assertThat(method.getName()).isEqualTo("apply");
117131
}
118132

119133
@Autowired
@@ -188,7 +202,23 @@ public IntegrationFlow routingGateway() {
188202

189203
}
190204

191-
interface MessageFunction extends Function<Object, Message<?>> {
205+
interface MessageFunction {
206+
207+
Message<?> apply(Object t);
208+
209+
default <V> Function<V, Message<?>> compose(Function<? super V, Object> before) {
210+
Objects.requireNonNull(before);
211+
return (v) -> apply(before.apply(v));
212+
}
213+
214+
default <V> Function<Object, V> andThen(Function<? super Message<?>, ? extends V> after) {
215+
Objects.requireNonNull(after);
216+
return (t) -> after.apply(apply(t));
217+
}
218+
219+
static <T> Function<T, T> identity() {
220+
return t -> t;
221+
}
192222

193223
}
194224

src/reference/asciidoc/gateway.adoc

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,12 @@ public interface Cafe {
351351

352352
If a method has no argument and no return value but does contain a payload expression, it is treated as a send-only operation.
353353

354+
[[gateway-calling-default-methods]]
355+
==== Invoking `default` Methods
356+
357+
An interface for gateway proxy may have `default` methods as well and starting with version 5.2.3, the framework injects a `DefaultMethodInvokingMethodInterceptor` into a proxy for calling `default` methods using a `java.lang.invoke.MethodHandle` approach instead of proxying them improperly.
358+
The interfaces from JDK, such as `java.util.function.Function`, still can be used for gateway proxy, but their `default` methods cannot be called because of internal Java security reasons for a `MethodHandles.Lookup` instantiation against JDK classes.
359+
354360
[[gateway-error-handling]]
355361
==== Error Handling
356362

@@ -719,7 +725,7 @@ A `Mono` can be used to retrieve the result later (similar to a `Future<?>`), or
719725
IMPORTANT: The `Mono` is not immediately flushed by the framework.
720726
Consequently, the underlying message flow is not started before the gateway method returns (as it is with a `Future<?>` `Executor` task).
721727
The flow starts when the `Mono` is subscribed to.
722-
Alternatively, the `Mono` (being a `Composable`) might be a part of Reactor stream, when the `subscribe()` is related to the entire `Flux`.
728+
Alternatively, the `Mono` (being a "`Composable`") might be a part of Reactor stream, when the `subscribe()` is related to the entire `Flux`.
723729
The following example shows how to create a gateway with Project Reactor:
724730

725731
====

0 commit comments

Comments
 (0)