Revised method selection for JMS listeners (and their parameters)
Issue: SPR-13576
This commit is contained in:
@@ -18,7 +18,6 @@ package org.springframework.jms.annotation;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -37,6 +36,7 @@ import org.springframework.beans.factory.NoSuchBeanDefinitionException;
|
||||
import org.springframework.beans.factory.SmartInitializingSingleton;
|
||||
import org.springframework.beans.factory.config.BeanPostProcessor;
|
||||
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
|
||||
import org.springframework.core.MethodIntrospector;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.jms.config.JmsListenerConfigUtils;
|
||||
@@ -48,7 +48,6 @@ import org.springframework.messaging.handler.annotation.support.DefaultMessageHa
|
||||
import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;
|
||||
import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
@@ -194,26 +193,30 @@ public class JmsListenerAnnotationBeanPostProcessor
|
||||
@Override
|
||||
public Object postProcessAfterInitialization(final Object bean, String beanName) throws BeansException {
|
||||
if (!this.nonAnnotatedClasses.contains(bean.getClass())) {
|
||||
final Set<Method> annotatedMethods = new LinkedHashSet<Method>(1);
|
||||
Class<?> targetClass = AopUtils.getTargetClass(bean);
|
||||
ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() {
|
||||
@Override
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
for (JmsListener jmsListener :
|
||||
AnnotationUtils.getRepeatableAnnotations(method, JmsListener.class, JmsListeners.class)) {
|
||||
processJmsListener(jmsListener, method, bean);
|
||||
annotatedMethods.add(method);
|
||||
}
|
||||
}
|
||||
});
|
||||
Map<Method, Set<JmsListener>> annotatedMethods = MethodIntrospector.selectMethods(targetClass,
|
||||
new MethodIntrospector.MetadataLookup<Set<JmsListener>>() {
|
||||
@Override
|
||||
public Set<JmsListener> inspect(Method method) {
|
||||
Set<JmsListener> listenerMethods =
|
||||
AnnotationUtils.getRepeatableAnnotations(method, JmsListener.class, JmsListeners.class);
|
||||
return (!listenerMethods.isEmpty() ? listenerMethods : null);
|
||||
}
|
||||
});
|
||||
if (annotatedMethods.isEmpty()) {
|
||||
this.nonAnnotatedClasses.add(bean.getClass());
|
||||
if (logger.isTraceEnabled()) {
|
||||
logger.trace("No @JmsListener annotations found on bean class: " + bean.getClass());
|
||||
logger.trace("No @JmsListener annotations found on bean type: " + bean.getClass());
|
||||
}
|
||||
}
|
||||
else {
|
||||
// Non-empty set of methods
|
||||
for (Map.Entry<Method, Set<JmsListener>> entry : annotatedMethods.entrySet()) {
|
||||
Method method = entry.getKey();
|
||||
for (JmsListener listener : entry.getValue()) {
|
||||
processJmsListener(listener, method, bean);
|
||||
}
|
||||
}
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug(annotatedMethods.size() + " @JmsListener methods processed on bean '" + beanName +
|
||||
"': " + annotatedMethods);
|
||||
@@ -223,29 +226,13 @@ public class JmsListenerAnnotationBeanPostProcessor
|
||||
return bean;
|
||||
}
|
||||
|
||||
protected void processJmsListener(JmsListener jmsListener, Method method, Object bean) {
|
||||
if (AopUtils.isJdkDynamicProxy(bean)) {
|
||||
try {
|
||||
// Found a @JmsListener method on the target class for this JDK proxy ->
|
||||
// is it also present on the proxy itself?
|
||||
method = bean.getClass().getMethod(method.getName(), method.getParameterTypes());
|
||||
}
|
||||
catch (SecurityException ex) {
|
||||
ReflectionUtils.handleReflectionException(ex);
|
||||
}
|
||||
catch (NoSuchMethodException ex) {
|
||||
throw new IllegalStateException(String.format(
|
||||
"@JmsListener method '%s' found on bean target class '%s', " +
|
||||
"but not found in any interface(s) for bean JDK proxy. Either " +
|
||||
"pull the method up to an interface or switch to subclass (CGLIB) " +
|
||||
"proxies by setting proxy-target-class/proxyTargetClass " +
|
||||
"attribute to 'true'", method.getName(), method.getDeclaringClass().getSimpleName()));
|
||||
}
|
||||
}
|
||||
protected void processJmsListener(JmsListener jmsListener, Method mostSpecificMethod, Object bean) {
|
||||
Method invocableMethod = MethodIntrospector.selectInvocableMethod(mostSpecificMethod, bean.getClass());
|
||||
|
||||
MethodJmsListenerEndpoint endpoint = new MethodJmsListenerEndpoint();
|
||||
endpoint.setBean(bean);
|
||||
endpoint.setMethod(method);
|
||||
endpoint.setMethod(invocableMethod);
|
||||
endpoint.setMostSpecificMethod(mostSpecificMethod);
|
||||
endpoint.setMessageHandlerMethodFactory(this.messageHandlerMethodFactory);
|
||||
endpoint.setBeanFactory(this.beanFactory);
|
||||
endpoint.setId(getEndpointId(jmsListener));
|
||||
@@ -268,9 +255,9 @@ public class JmsListenerAnnotationBeanPostProcessor
|
||||
factory = this.beanFactory.getBean(containerFactoryBeanName, JmsListenerContainerFactory.class);
|
||||
}
|
||||
catch (NoSuchBeanDefinitionException ex) {
|
||||
throw new BeanInitializationException("Could not register jms listener endpoint on [" +
|
||||
method + "], no " + JmsListenerContainerFactory.class.getSimpleName() + " with id '" +
|
||||
containerFactoryBeanName + "' was found in the application context", ex);
|
||||
throw new BeanInitializationException("Could not register JMS listener endpoint on [" +
|
||||
mostSpecificMethod + "], no " + JmsListenerContainerFactory.class.getSimpleName() +
|
||||
" with id '" + containerFactoryBeanName + "' was found in the application context", ex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import org.springframework.util.StringUtils;
|
||||
* an incoming message for this endpoint.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @author Juergen Hoeller
|
||||
* @since 4.1
|
||||
*/
|
||||
public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint {
|
||||
@@ -47,6 +48,8 @@ public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint {
|
||||
|
||||
private Method method;
|
||||
|
||||
private Method mostSpecificMethod;
|
||||
|
||||
private MessageHandlerMethodFactory messageHandlerMethodFactory;
|
||||
|
||||
private BeanFactory beanFactory;
|
||||
@@ -74,6 +77,29 @@ public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint {
|
||||
return this.method;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the most specific method known for this endpoint's declaration.
|
||||
* <p>In case of a proxy, this will be the method on the target class
|
||||
* (if annotated itself, that is, if not just annotated in an interface).
|
||||
* @since 4.2.3
|
||||
*/
|
||||
public void setMostSpecificMethod(Method mostSpecificMethod) {
|
||||
this.mostSpecificMethod = mostSpecificMethod;
|
||||
}
|
||||
|
||||
public Method getMostSpecificMethod() {
|
||||
if (this.mostSpecificMethod != null) {
|
||||
return this.mostSpecificMethod;
|
||||
}
|
||||
else if (AopUtils.isAopProxy(this.bean)) {
|
||||
Class<?> target = AopProxyUtils.ultimateTargetClass(this.bean);
|
||||
return AopUtils.getMostSpecificMethod(getMethod(), target);
|
||||
}
|
||||
else {
|
||||
return getMethod();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the {@link MessageHandlerMethodFactory} to use to build the
|
||||
* {@link InvocableHandlerMethod} responsible to manage the invocation
|
||||
@@ -134,8 +160,8 @@ public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint {
|
||||
if (ann != null) {
|
||||
Object[] destinations = ann.value();
|
||||
if (destinations.length != 1) {
|
||||
throw new IllegalStateException("Invalid @" + SendTo.class.getSimpleName() + " annotation on '"
|
||||
+ specificMethod + "' one destination must be set (got " + Arrays.toString(destinations) + ")");
|
||||
throw new IllegalStateException("Invalid @" + SendTo.class.getSimpleName() + " annotation on '" +
|
||||
specificMethod + "' one destination must be set (got " + Arrays.toString(destinations) + ")");
|
||||
}
|
||||
return resolve((String) destinations[0]);
|
||||
}
|
||||
@@ -154,16 +180,6 @@ public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint {
|
||||
}
|
||||
|
||||
|
||||
private Method getMostSpecificMethod() {
|
||||
if (AopUtils.isAopProxy(this.bean)) {
|
||||
Class<?> target = AopProxyUtils.ultimateTargetClass(this.bean);
|
||||
return AopUtils.getMostSpecificMethod(getMethod(), target);
|
||||
}
|
||||
else {
|
||||
return getMethod();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StringBuilder getEndpointDescription() {
|
||||
return super.getEndpointDescription()
|
||||
|
||||
Reference in New Issue
Block a user