INT-3825: Messaging Ann. on Non-public Methods
JIRA: https://jira.spring.io/browse/INT-3825 Rework `MessagingMethodInvokerHelper` logic to accept non-public methods with Messaging annotations as candidates for invocation Adjust SpEL configuration in the `MessagingMethodInvokerHelper` to deal with `declaredMethods()`, not only `public` Honor caching in the `MethodReference`
This commit is contained in:
committed by
Gary Russell
parent
8f0fa3468f
commit
8d94bd5e3e
@@ -34,6 +34,7 @@ import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
@@ -51,6 +52,7 @@ import org.springframework.expression.EvaluationException;
|
||||
import org.springframework.expression.Expression;
|
||||
import org.springframework.expression.TypeConverter;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.expression.spel.support.ReflectiveMethodResolver;
|
||||
import org.springframework.expression.spel.support.StandardEvaluationContext;
|
||||
import org.springframework.integration.annotation.Payloads;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
@@ -119,7 +121,6 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
|
||||
private Method method;
|
||||
|
||||
|
||||
public MessagingMethodInvokerHelper(Object targetObject, Method method, Class<?> expectedType,
|
||||
boolean canProcessMessageList) {
|
||||
this(targetObject, null, method, expectedType, canProcessMessageList);
|
||||
@@ -223,7 +224,7 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
this.targetObject = targetObject;
|
||||
this.requiresReply = expectedType != null;
|
||||
Map<String, Map<Class<?>, HandlerMethod>> handlerMethodsForTarget =
|
||||
this.findHandlerMethodsForTarget(targetObject, annotationType, methodName, this.requiresReply);
|
||||
findHandlerMethodsForTarget(targetObject, annotationType, methodName, this.requiresReply);
|
||||
Map<Class<?>, HandlerMethod> handlerMethods = handlerMethodsForTarget.get(CANDIDATE_METHODS);
|
||||
Map<Class<?>, HandlerMethod> handlerMessageMethods = handlerMethodsForTarget.get(CANDIDATE_MESSAGE_METHODS);
|
||||
if ((handlerMethods.size() == 1 && handlerMessageMethods.isEmpty()) ||
|
||||
@@ -242,13 +243,13 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
this.handlerMethod = null;
|
||||
this.handlerMethods = handlerMethods;
|
||||
this.handlerMessageMethods = handlerMessageMethods;
|
||||
this.handlerMethodsList = new LinkedList<Map<Class<?>, HandlerMethod>>();
|
||||
this.handlerMethodsList = new LinkedList<>();
|
||||
|
||||
//TODO Consider to use global option to determine a precedence of methods
|
||||
this.handlerMethodsList.add(this.handlerMethods);
|
||||
this.handlerMethodsList.add(this.handlerMessageMethods);
|
||||
}
|
||||
this.setDisplayString(targetObject, methodName);
|
||||
setDisplayString(targetObject, methodName);
|
||||
}
|
||||
|
||||
private void setDisplayString(Object targetObject, Object targetMethod) {
|
||||
@@ -267,8 +268,22 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
private void prepareEvaluationContext() {
|
||||
StandardEvaluationContext context = getEvaluationContext(false);
|
||||
Class<?> targetType = AopUtils.getTargetClass(this.targetObject);
|
||||
|
||||
ReflectiveMethodResolver declaredMethodResolver = new ReflectiveMethodResolver() {
|
||||
|
||||
@Override
|
||||
protected Method[] getMethods(Class<?> type) {
|
||||
return Stream.of(type.getMethods(), type.getDeclaredMethods())
|
||||
.flatMap(Stream::of)
|
||||
.toArray(Method[]::new);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
if (this.method != null) {
|
||||
context.registerMethodFilter(targetType, new FixedMethodFilter(this.method));
|
||||
FixedMethodFilter fixedMethodFilter = new FixedMethodFilter(this.method);
|
||||
context.registerMethodFilter(targetType, fixedMethodFilter);
|
||||
declaredMethodResolver.registerMethodFilter(targetType, fixedMethodFilter);
|
||||
if (this.expectedType != null) {
|
||||
Assert.state(context.getTypeConverter()
|
||||
.canConvert(TypeDescriptor.valueOf((this.method).getReturnType()),
|
||||
@@ -277,13 +292,15 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
}
|
||||
}
|
||||
else {
|
||||
AnnotatedMethodFilter filter = new AnnotatedMethodFilter(this.annotationType, this.methodName,
|
||||
this.requiresReply);
|
||||
Assert.state(canReturnExpectedType(filter, targetType, context.getTypeConverter()),
|
||||
AnnotatedMethodFilter annotatedMethodFilter = new AnnotatedMethodFilter(this.annotationType,
|
||||
this.methodName, this.requiresReply);
|
||||
Assert.state(canReturnExpectedType(annotatedMethodFilter, targetType, context.getTypeConverter()),
|
||||
"Cannot convert to expected type (" + this.expectedType + ") from " + this.method);
|
||||
context.registerMethodFilter(targetType, filter);
|
||||
context.registerMethodFilter(targetType, annotatedMethodFilter);
|
||||
declaredMethodResolver.registerMethodFilter(targetType, annotatedMethodFilter);
|
||||
}
|
||||
context.setVariable("target", this.targetObject);
|
||||
context.setMethodResolvers(Collections.singletonList(declaredMethodResolver));
|
||||
}
|
||||
|
||||
private boolean canReturnExpectedType(AnnotatedMethodFilter filter, Class<?> targetType,
|
||||
@@ -316,7 +333,7 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
Class<?> expectedType = this.expectedType != null ? this.expectedType : candidate.method.getReturnType();
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
T result = (T) this.evaluateExpression(expression, parameters, expectedType);
|
||||
T result = (T) evaluateExpression(expression, parameters, expectedType);
|
||||
if (this.requiresReply) {
|
||||
Assert.notNull(result,
|
||||
"Expression evaluation result was null, but this processor requires a reply.");
|
||||
@@ -348,7 +365,7 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
final Map<Class<?>, HandlerMethod> fallbackMessageMethods = new HashMap<Class<?>, HandlerMethod>();
|
||||
final AtomicReference<Class<?>> ambiguousFallbackType = new AtomicReference<Class<?>>();
|
||||
final AtomicReference<Class<?>> ambiguousFallbackMessageGenericType = new AtomicReference<Class<?>>();
|
||||
final Class<?> targetClass = this.getTargetClass(targetObject);
|
||||
final Class<?> targetClass = getTargetClass(targetObject);
|
||||
MethodFilter methodFilter = new UniqueMethodFilter(targetClass);
|
||||
ReflectionUtils.doWithMethods(targetClass, method1 -> {
|
||||
boolean matchesAnnotation = false;
|
||||
@@ -361,7 +378,10 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
if (method1.getDeclaringClass().equals(Proxy.class)) {
|
||||
return;
|
||||
}
|
||||
if (!Modifier.isPublic(method1.getModifiers())) {
|
||||
if (annotationType != null && AnnotationUtils.findAnnotation(method1, annotationType) != null) {
|
||||
matchesAnnotation = true;
|
||||
}
|
||||
else if (!Modifier.isPublic(method1.getModifiers())) {
|
||||
return;
|
||||
}
|
||||
if (requiresReply && void.class.equals(method1.getReturnType())) {
|
||||
@@ -374,9 +394,6 @@ public class MessagingMethodInvokerHelper<T> extends AbstractExpressionEvaluator
|
||||
&& ObjectUtils.containsElement(new String[] { "start", "stop", "isRunning" }, method1.getName())) {
|
||||
return;
|
||||
}
|
||||
if (annotationType != null && AnnotationUtils.findAnnotation(method1, annotationType) != null) {
|
||||
matchesAnnotation = true;
|
||||
}
|
||||
HandlerMethod handlerMethod1 = null;
|
||||
try {
|
||||
handlerMethod1 = new HandlerMethod(method1, this.canProcessMessageList);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright 2002-2010 the original author or authors.
|
||||
* Copyright 2002-2016 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,13 +18,14 @@ package org.springframework.integration.util;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.ReflectionUtils.MethodFilter;
|
||||
|
||||
/**
|
||||
* @author Oleg Zhurakousky
|
||||
* @author Artem Bilan
|
||||
* @since 2.0
|
||||
*/
|
||||
public class UniqueMethodFilter implements MethodFilter {
|
||||
@@ -33,7 +34,7 @@ public class UniqueMethodFilter implements MethodFilter {
|
||||
|
||||
|
||||
public UniqueMethodFilter(Class<?> targetClass) {
|
||||
ArrayList<Method> allMethods = new ArrayList<Method>(Arrays.asList(targetClass.getMethods()));
|
||||
Method[] allMethods = ReflectionUtils.getAllDeclaredMethods(targetClass);
|
||||
for (Method method : allMethods) {
|
||||
this.uniqueMethods.add(org.springframework.util.ClassUtils.getMostSpecificMethod(method, targetClass));
|
||||
}
|
||||
|
||||
@@ -550,6 +550,23 @@ public class MethodInvokingMessageProcessorTests {
|
||||
assertEquals("FOO", targetObject.arguments.get("foo2"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPrivateMethod() throws Exception {
|
||||
class Foo {
|
||||
|
||||
@ServiceActivator
|
||||
private String service(String payload) {
|
||||
return payload.toUpperCase();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MessagingMethodInvokerHelper helper = new MessagingMethodInvokerHelper(new Foo(), ServiceActivator.class, false);
|
||||
|
||||
assertEquals("FOO", helper.process(new GenericMessage<>("foo")));
|
||||
assertEquals("BAR", helper.process(new GenericMessage<>("bar")));
|
||||
}
|
||||
|
||||
private static class ExceptionCauseMatcher extends TypeSafeMatcher<Exception> {
|
||||
private Throwable cause;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user