diff --git a/spring-jms/src/main/java/org/springframework/jms/config/MethodJmsListenerEndpoint.java b/spring-jms/src/main/java/org/springframework/jms/config/MethodJmsListenerEndpoint.java index f22389fa15..29c9dec455 100644 --- a/spring-jms/src/main/java/org/springframework/jms/config/MethodJmsListenerEndpoint.java +++ b/spring-jms/src/main/java/org/springframework/jms/config/MethodJmsListenerEndpoint.java @@ -19,6 +19,8 @@ package org.springframework.jms.config; import java.lang.reflect.Method; import java.util.Arrays; +import org.springframework.aop.framework.AopProxyUtils; +import org.springframework.aop.support.AopUtils; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.jms.listener.MessageListenerContainer; import org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter; @@ -109,18 +111,29 @@ public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint { } private String getDefaultResponseDestination() { - SendTo ann = AnnotationUtils.getAnnotation(getMethod(), SendTo.class); + Method specificMethod = getMostSpecificMethod(); + SendTo ann = AnnotationUtils.getAnnotation(specificMethod, SendTo.class); if (ann != null) { Object[] destinations = ann.value(); if (destinations.length != 1) { throw new IllegalStateException("Invalid @" + SendTo.class.getSimpleName() + " annotation on '" - + getMethod() + "' one destination must be set (got " + Arrays.toString(destinations) + ")"); + + specificMethod + "' one destination must be set (got " + Arrays.toString(destinations) + ")"); } return (String) destinations[0]; } return null; } + 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() diff --git a/spring-jms/src/test/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessorTests.java b/spring-jms/src/test/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessorTests.java index fe96e08b82..6c313b4f0d 100644 --- a/spring-jms/src/test/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessorTests.java +++ b/spring-jms/src/test/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessorTests.java @@ -20,9 +20,13 @@ import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; +import java.lang.reflect.Method; +import org.junit.Rule; import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.springframework.beans.factory.BeanCreationException; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.Bean; @@ -34,9 +38,16 @@ import org.springframework.jms.config.JmsListenerEndpointRegistry; import org.springframework.jms.config.MessageListenerTestContainer; import org.springframework.jms.config.MethodJmsListenerEndpoint; import org.springframework.jms.listener.SimpleMessageListenerContainer; +import org.springframework.messaging.handler.annotation.SendTo; import org.springframework.stereotype.Component; +import org.springframework.transaction.PlatformTransactionManager; +import org.springframework.transaction.annotation.EnableTransactionManagement; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.ReflectionUtils; +import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import static org.mockito.Mockito.*; /** * @author Stephane Nicoll @@ -44,6 +55,9 @@ import static org.junit.Assert.*; */ public class JmsListenerAnnotationBeanPostProcessorTests { + @Rule + public final ExpectedException thrown = ExpectedException.none(); + @Test public void simpleMessageListener() { ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( @@ -73,10 +87,42 @@ public class JmsListenerAnnotationBeanPostProcessorTests { ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( Config.class, MetaAnnotationTestBean.class); - JmsListenerContainerTestFactory factory = context.getBean(JmsListenerContainerTestFactory.class); - assertEquals("one container should have been registered", 1, factory.getListenerContainers().size()); - JmsListenerEndpoint endpoint = factory.getListenerContainers().get(0).getEndpoint(); - assertEquals("metaTestQueue", ((AbstractJmsListenerEndpoint) endpoint).getDestination()); + try { + JmsListenerContainerTestFactory factory = context.getBean(JmsListenerContainerTestFactory.class); + assertEquals("one container should have been registered", 1, factory.getListenerContainers().size()); + JmsListenerEndpoint endpoint = factory.getListenerContainers().get(0).getEndpoint(); + assertEquals("metaTestQueue", ((AbstractJmsListenerEndpoint) endpoint).getDestination()); + } + finally { + context.close(); + } + } + + @Test + public void sendToAnnotationFoundOnProxy() { + ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( + Config.class, ProxyConfig.class, ProxyTestBean.class); + try { + JmsListenerContainerTestFactory factory = context.getBean(JmsListenerContainerTestFactory.class); + assertEquals("one container should have been registered", 1, factory.getListenerContainers().size()); + JmsListenerEndpoint endpoint = factory.getListenerContainers().get(0).getEndpoint(); + Method m = ReflectionUtils.findMethod(endpoint.getClass(), "getDefaultResponseDestination"); + ReflectionUtils.makeAccessible(m); + Object destination = ReflectionUtils.invokeMethod(m, endpoint); + assertEquals("SendTo annotation not found on proxy", "foobar", destination); + } + finally { + context.close(); + } + } + + @Test + public void invalidProxy() { + thrown.expect(BeanCreationException.class); + thrown.expectCause(is(instanceOf(IllegalStateException.class))); + thrown.expectMessage("handleIt2"); + new AnnotationConfigApplicationContext( + Config.class, ProxyConfig.class, InvalidProxyTestBean.class); } @@ -102,7 +148,7 @@ public class JmsListenerAnnotationBeanPostProcessorTests { @JmsListener(destination = "metaTestQueue") @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) - static @interface FooListener { + @interface FooListener { } @@ -128,4 +174,47 @@ public class JmsListenerAnnotationBeanPostProcessorTests { } } + @Configuration + @EnableTransactionManagement + static class ProxyConfig { + + @Bean + public PlatformTransactionManager transactionManager() { + return mock(PlatformTransactionManager.class); + } + + } + + interface SimpleService { + + void handleIt(String body); + + } + + @Component + static class ProxyTestBean implements SimpleService { + + @Override + @Transactional + @JmsListener(destination = "testQueue") + @SendTo("foobar") + public void handleIt(String body) { + + } + } + + @Component + static class InvalidProxyTestBean implements SimpleService { + + @Override + public void handleIt(String body) { + } + + @Transactional + @JmsListener(destination = "testQueue") + @SendTo("foobar") + public void handleIt2(String body) { + } + } + }