GH-3660: Invoke well-known lambdas explicitly
Fixes https://github.com/spring-projects/spring-integration/pull/3660 * Change the `LambdaMessageProcessor` to invoke `Function`, `GenericHandler`, `GenericSelector` & `GenericTransformer` explicitly without reflection. This allows to avoid `--add-opens` for Java types to be used from reflections. Plus this removes some overhead with native images. And in general it is faster than reflection invocation.
This commit is contained in:
committed by
Gary Russell
parent
1fbfad2d2a
commit
f36181bc5a
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2016-2021 the original author or authors.
|
||||
* Copyright 2016-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.
|
||||
@@ -21,6 +21,7 @@ import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -31,8 +32,11 @@ import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.core.MethodIntrospector;
|
||||
import org.springframework.core.log.LogMessage;
|
||||
import org.springframework.integration.context.IntegrationContextUtils;
|
||||
import org.springframework.integration.core.GenericSelector;
|
||||
import org.springframework.integration.transformer.GenericTransformer;
|
||||
import org.springframework.lang.Nullable;
|
||||
import org.springframework.messaging.Message;
|
||||
import org.springframework.messaging.MessageHeaders;
|
||||
import org.springframework.messaging.converter.MessageConverter;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ClassUtils;
|
||||
@@ -85,11 +89,20 @@ public class LambdaMessageProcessor implements MessageProcessor<Object>, BeanFac
|
||||
"classes with single method - functional interface implementations.");
|
||||
|
||||
this.method = methods.iterator().next();
|
||||
ReflectionUtils.makeAccessible(this.method);
|
||||
if (!isExplicit(target)) {
|
||||
ReflectionUtils.makeAccessible(this.method);
|
||||
}
|
||||
this.parameterTypes = this.method.getParameterTypes();
|
||||
this.expectedType = expectedType;
|
||||
}
|
||||
|
||||
private static boolean isExplicit(Object target) {
|
||||
return target instanceof Function<?, ?> ||
|
||||
target instanceof GenericHandler<?> ||
|
||||
target instanceof GenericSelector<?> ||
|
||||
target instanceof GenericTransformer<?, ?>;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
|
||||
this.messageConverter =
|
||||
@@ -102,20 +115,22 @@ public class LambdaMessageProcessor implements MessageProcessor<Object>, BeanFac
|
||||
Object[] args = buildArgs(message);
|
||||
|
||||
try {
|
||||
Object result = this.method.invoke(this.target, args);
|
||||
Object result = invokeMethod(args);
|
||||
if (result != null && org.springframework.integration.util.ClassUtils.isKotlinUnit(result.getClass())) {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
catch (RuntimeException ex) {
|
||||
if (ex instanceof ClassCastException classCastException) {
|
||||
logClassCastException(classCastException);
|
||||
}
|
||||
throw ex;
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
final Throwable cause = e.getCause();
|
||||
if (e.getTargetException() instanceof ClassCastException) {
|
||||
LOGGER.error("Could not invoke the method '" + this.method + "' due to a class cast exception, " +
|
||||
"if using a lambda in the DSL, consider using an overloaded EIP method " +
|
||||
"that takes a Class<?> argument to explicitly specify the type. " +
|
||||
"An example of when this often occurs is if the lambda is configured to " +
|
||||
"receive a Message<?> argument.", cause);
|
||||
if (e.getTargetException() instanceof ClassCastException classCastException) {
|
||||
logClassCastException(classCastException);
|
||||
}
|
||||
if (cause instanceof RuntimeException) { // NOSONAR
|
||||
throw (RuntimeException) cause;
|
||||
@@ -170,4 +185,41 @@ public class LambdaMessageProcessor implements MessageProcessor<Object>, BeanFac
|
||||
return args;
|
||||
}
|
||||
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
private Object invokeMethod(Object[] args) throws InvocationTargetException, IllegalAccessException {
|
||||
if (this.target instanceof Function function) {
|
||||
return function.apply(args[0]);
|
||||
}
|
||||
else if (this.target instanceof GenericSelector selector) {
|
||||
return selector.accept(args[0]);
|
||||
}
|
||||
else if (this.target instanceof GenericTransformer transformer) {
|
||||
return transformer.transform(args[0]);
|
||||
}
|
||||
else if (this.target instanceof GenericHandler handler) {
|
||||
return handler.handle(args[0], (MessageHeaders) args[1]);
|
||||
}
|
||||
else {
|
||||
return this.method.invoke(this.target, args);
|
||||
}
|
||||
|
||||
/* TODO when preview features are available in the next Java version
|
||||
return switch (this.target) {
|
||||
case Function function -> function.apply(args[0]);
|
||||
case GenericSelector selector -> selector.accept(args[0]);
|
||||
case GenericTransformer transformer -> transformer.transform(args[0]);
|
||||
case GenericHandler handler -> handler.handle(args[0], (MessageHeaders) args[1]);
|
||||
default -> this.method.invoke(this.target, args);
|
||||
};
|
||||
*/
|
||||
}
|
||||
|
||||
private void logClassCastException(ClassCastException classCastException) {
|
||||
LOGGER.error("Could not invoke the method '" + this.method + "' due to a class cast exception, " +
|
||||
"if using a lambda in the DSL, consider using an overloaded EIP method " +
|
||||
"that takes a Class<?> argument to explicitly specify the type. " +
|
||||
"An example of when this often occurs is if the lambda is configured to " +
|
||||
"receive a Message<?> argument.", classCastException);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import java.util.Date;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
@@ -107,7 +106,6 @@ public class LambdaMessageProcessorTests {
|
||||
|
||||
|
||||
@Test
|
||||
@Disabled("Until https://github.com/spring-projects/spring-integration/issues/3660")
|
||||
public void testCustomConverter() {
|
||||
LambdaMessageProcessor lmp = new LambdaMessageProcessor(Function.identity(), TestPojo.class);
|
||||
lmp.setBeanFactory(this.beanFactory);
|
||||
|
||||
Reference in New Issue
Block a user