diff --git a/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java b/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java index 0b5d6056de..db7b7c40d1 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/util/MessagingMethodInvokerHelper.java @@ -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 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 extends AbstractExpressionEvaluator this.targetObject = targetObject; this.requiresReply = expectedType != null; Map, HandlerMethod>> handlerMethodsForTarget = - this.findHandlerMethodsForTarget(targetObject, annotationType, methodName, this.requiresReply); + findHandlerMethodsForTarget(targetObject, annotationType, methodName, this.requiresReply); Map, HandlerMethod> handlerMethods = handlerMethodsForTarget.get(CANDIDATE_METHODS); Map, HandlerMethod> handlerMessageMethods = handlerMethodsForTarget.get(CANDIDATE_MESSAGE_METHODS); if ((handlerMethods.size() == 1 && handlerMessageMethods.isEmpty()) || @@ -242,13 +243,13 @@ public class MessagingMethodInvokerHelper extends AbstractExpressionEvaluator this.handlerMethod = null; this.handlerMethods = handlerMethods; this.handlerMessageMethods = handlerMessageMethods; - this.handlerMethodsList = new LinkedList, 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 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 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 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 extends AbstractExpressionEvaluator final Map, HandlerMethod> fallbackMessageMethods = new HashMap, HandlerMethod>(); final AtomicReference> ambiguousFallbackType = new AtomicReference>(); final AtomicReference> ambiguousFallbackMessageGenericType = new AtomicReference>(); - 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 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 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); diff --git a/spring-integration-core/src/main/java/org/springframework/integration/util/UniqueMethodFilter.java b/spring-integration-core/src/main/java/org/springframework/integration/util/UniqueMethodFilter.java index f7cd3781fa..9d632a2c8b 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/util/UniqueMethodFilter.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/util/UniqueMethodFilter.java @@ -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 allMethods = new ArrayList(Arrays.asList(targetClass.getMethods())); + Method[] allMethods = ReflectionUtils.getAllDeclaredMethods(targetClass); for (Method method : allMethods) { this.uniqueMethods.add(org.springframework.util.ClassUtils.getMostSpecificMethod(method, targetClass)); } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/handler/MethodInvokingMessageProcessorTests.java b/spring-integration-core/src/test/java/org/springframework/integration/handler/MethodInvokingMessageProcessorTests.java index f24dc7d7da..f9fec67b85 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/handler/MethodInvokingMessageProcessorTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/handler/MethodInvokingMessageProcessorTests.java @@ -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 { private Throwable cause;