INT-4456: Explicit methods for Kotlin lambdas

JIRA: https://jira.spring.io/browse/INT-4456

Since Kotlin compiles lambdas different way than Java, they are not
synthetic classes at runtime anymore.
Therefore we fallback to the method invocation logic, but since Java 8
interfaces has `default` methods as well the `MessagingMethodInvokerHelper`
can't select the target method for invocation

* Use explicit `Function.apply()` for non-synthetic implementations
* Add `RouterDslTests` Kotlin-based test-case

**Cherry-pick to 5.0.x**

* Add more explicit methods for invokers
This commit is contained in:
Artem Bilan
2018-04-26 22:32:27 -04:00
committed by Gary Russell
parent 000f2972f4
commit 0c1423e78a
5 changed files with 171 additions and 29 deletions

View File

@@ -86,6 +86,7 @@ import org.springframework.integration.transformer.HeaderFilter;
import org.springframework.integration.transformer.MessageTransformingHandler;
import org.springframework.integration.transformer.MethodInvokingTransformer;
import org.springframework.integration.transformer.Transformer;
import org.springframework.integration.util.ClassUtils;
import org.springframework.messaging.Message;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
@@ -633,7 +634,7 @@ public abstract class IntegrationFlowDefinition<B extends IntegrationFlowDefinit
Transformer transformer = genericTransformer instanceof Transformer ? (Transformer) genericTransformer :
(isLambda(genericTransformer)
? new MethodInvokingTransformer(new LambdaMessageProcessor(genericTransformer, payloadType))
: new MethodInvokingTransformer(genericTransformer));
: new MethodInvokingTransformer(genericTransformer, ClassUtils.TRANSFORMER_TRANSFORM_METHOD));
return addComponent(transformer)
.handle(new MessageTransformingHandler(transformer), endpointConfigurer);
}
@@ -826,7 +827,7 @@ public abstract class IntegrationFlowDefinition<B extends IntegrationFlowDefinit
MessageSelector selector = genericSelector instanceof MessageSelector ? (MessageSelector) genericSelector :
(isLambda(genericSelector)
? new MethodInvokingSelector(new LambdaMessageProcessor(genericSelector, payloadType))
: new MethodInvokingSelector(genericSelector));
: new MethodInvokingSelector(genericSelector, ClassUtils.SELECTOR_ACCEPT_METHOD));
return this.register(new FilterEndpointSpec(new MessageFilter(selector)), endpointConfigurer);
}
@@ -1022,12 +1023,12 @@ public abstract class IntegrationFlowDefinition<B extends IntegrationFlowDefinit
*/
public <P> B handle(Class<P> payloadType, GenericHandler<P> handler,
Consumer<GenericEndpointSpec<ServiceActivatingHandler>> endpointConfigurer) {
ServiceActivatingHandler serviceActivatingHandler = null;
ServiceActivatingHandler serviceActivatingHandler;
if (isLambda(handler)) {
serviceActivatingHandler = new ServiceActivatingHandler(new LambdaMessageProcessor(handler, payloadType));
}
else {
serviceActivatingHandler = new ServiceActivatingHandler(handler, "handle");
serviceActivatingHandler = new ServiceActivatingHandler(handler, ClassUtils.HANDLER_HANDLE_METHOD);
}
return this.handle(serviceActivatingHandler, endpointConfigurer);
}
@@ -1533,7 +1534,7 @@ public abstract class IntegrationFlowDefinition<B extends IntegrationFlowDefinit
Consumer<SplitterEndpointSpec<MethodInvokingSplitter>> endpointConfigurer) {
MethodInvokingSplitter split = isLambda(splitter)
? new MethodInvokingSplitter(new LambdaMessageProcessor(splitter, payloadType))
: new MethodInvokingSplitter(splitter);
: new MethodInvokingSplitter(splitter, ClassUtils.FUNCTION_APPLY_METHOD);
return this.split(split, endpointConfigurer);
}
@@ -1933,7 +1934,7 @@ public abstract class IntegrationFlowDefinition<B extends IntegrationFlowDefinit
Consumer<RouterSpec<T, MethodInvokingRouter>> routerConfigurer) {
MethodInvokingRouter methodInvokingRouter = isLambda(router)
? new MethodInvokingRouter(new LambdaMessageProcessor(router, payloadType))
: new MethodInvokingRouter(router);
: new MethodInvokingRouter(router, ClassUtils.FUNCTION_APPLY_METHOD);
return route(new RouterSpec<>(methodInvokingRouter), routerConfigurer);
}

View File

@@ -21,8 +21,8 @@ import org.springframework.integration.core.GenericSelector;
import org.springframework.integration.core.MessageSelector;
import org.springframework.integration.filter.ExpressionEvaluatingSelector;
import org.springframework.integration.filter.MethodInvokingSelector;
import org.springframework.integration.handler.LambdaMessageProcessor;
import org.springframework.integration.router.RecipientListRouter;
import org.springframework.integration.util.ClassUtils;
import org.springframework.messaging.MessageChannel;
import org.springframework.util.StringUtils;
@@ -96,23 +96,20 @@ public class RecipientListRouterSpec extends AbstractRouterSpec<RecipientListRou
* @return the router spec.
*/
public <P> RecipientListRouterSpec recipient(String channelName, GenericSelector<P> selector) {
MessageSelector messageSelector = wrapToMessageSelectorIfNecessary(selector);
this.handler.addRecipient(channelName, messageSelector);
return _this();
}
private <P> MessageSelector wrapToMessageSelectorIfNecessary(GenericSelector<P> selector) {
MessageSelector messageSelector;
if (selector instanceof MessageSelector) {
messageSelector = (MessageSelector) selector;
}
else {
messageSelector =
isLambda(selector)
? new MethodInvokingSelector(new LambdaMessageProcessor(selector, null))
: new MethodInvokingSelector(selector);
messageSelector = new MethodInvokingSelector(selector, ClassUtils.SELECTOR_ACCEPT_METHOD);
}
this.handler.addRecipient(channelName, messageSelector);
return _this();
}
private static boolean isLambda(Object o) {
Class<?> aClass = o.getClass();
return aClass.isSynthetic() && !aClass.isAnonymousClass() && !aClass.isLocalClass();
return messageSelector;
}
/**
@@ -170,16 +167,7 @@ public class RecipientListRouterSpec extends AbstractRouterSpec<RecipientListRou
* @return the router spec.
*/
public <P> RecipientListRouterSpec recipient(MessageChannel channel, GenericSelector<P> selector) {
MessageSelector messageSelector;
if (selector instanceof MessageSelector) {
messageSelector = (MessageSelector) selector;
}
else {
messageSelector =
isLambda(selector)
? new MethodInvokingSelector(new LambdaMessageProcessor(selector, null))
: new MethodInvokingSelector(selector);
}
MessageSelector messageSelector = wrapToMessageSelectorIfNecessary(selector);
this.handler.addRecipient(channel, messageSelector);
return _this();
}

View File

@@ -1,5 +1,5 @@
/*
* Copyright 2002-2013 the original author or authors.
* Copyright 2002-2018 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.
@@ -16,16 +16,63 @@
package org.springframework.integration.util;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import org.springframework.integration.core.GenericSelector;
import org.springframework.integration.transformer.GenericTransformer;
import org.springframework.util.ReflectionUtils;
/**
* @author Mark Fisher
* @author Artem Bilan
*
* @since 2.0
*/
public abstract class ClassUtils {
/**
* The {@link Function#apply(Object)} method object.
*/
public static final Method FUNCTION_APPLY_METHOD =
ReflectionUtils.findMethod(Function.class, "apply", (Class<?>[]) null);
/**
* The {@link GenericSelector#accept(Object)} method object.
*/
public static final Method SELECTOR_ACCEPT_METHOD =
ReflectionUtils.findMethod(GenericSelector.class, "accept", (Class<?>[]) null);
/**
* The {@link GenericSelector#accept(Object)} method object.
*/
public static final Method TRANSFORMER_TRANSFORM_METHOD =
ReflectionUtils.findMethod(GenericTransformer.class, "transform", (Class<?>[]) null);
/**
* The {@code org.springframework.integration.handler.GenericHandler#handle(Object, Map)} method object.
*/
public static final Method HANDLER_HANDLE_METHOD;
static {
Class<?> genericHandlerClass = null;
try {
genericHandlerClass =
org.springframework.util.ClassUtils.forName(
"org.springframework.integration.handler.GenericHandler",
org.springframework.util.ClassUtils.getDefaultClassLoader());
}
catch (ClassNotFoundException e) {
ReflectionUtils.rethrowRuntimeException(e);
}
HANDLER_HANDLE_METHOD = ReflectionUtils.findMethod(genericHandlerClass, "handle", (Class<?>[]) null);
}
/**
* Map with primitive wrapper type as key and corresponding primitive
* type as value, for example: Integer.class -> int.class.