Skip to content

Commit adc7ad7

Browse files
committed
Fix detection of the @sendto annotation
Previously, the default reply destination could not be discovered if the @JmsListener annotation was placed on a bean that is eligible for proxying as the proxy method is used internally and does not reveal an annotation placed on the implementation. This commit makes sure to resolve the most specific method when searching that annotation. Issue: SPR-12513
1 parent b796c1e commit adc7ad7

File tree

2 files changed

+109
-7
lines changed

2 files changed

+109
-7
lines changed

spring-jms/src/main/java/org/springframework/jms/config/MethodJmsListenerEndpoint.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919
import java.lang.reflect.Method;
2020
import java.util.Arrays;
2121

22+
import org.springframework.aop.framework.AopProxyUtils;
23+
import org.springframework.aop.support.AopUtils;
2224
import org.springframework.core.annotation.AnnotationUtils;
2325
import org.springframework.jms.listener.MessageListenerContainer;
2426
import org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter;
@@ -109,18 +111,29 @@ protected MessagingMessageListenerAdapter createMessageListenerInstance() {
109111
}
110112

111113
private String getDefaultResponseDestination() {
112-
SendTo ann = AnnotationUtils.getAnnotation(getMethod(), SendTo.class);
114+
Method specificMethod = getMostSpecificMethod();
115+
SendTo ann = AnnotationUtils.getAnnotation(specificMethod, SendTo.class);
113116
if (ann != null) {
114117
Object[] destinations = ann.value();
115118
if (destinations.length != 1) {
116119
throw new IllegalStateException("Invalid @" + SendTo.class.getSimpleName() + " annotation on '"
117-
+ getMethod() + "' one destination must be set (got " + Arrays.toString(destinations) + ")");
120+
+ specificMethod + "' one destination must be set (got " + Arrays.toString(destinations) + ")");
118121
}
119122
return (String) destinations[0];
120123
}
121124
return null;
122125
}
123126

127+
private Method getMostSpecificMethod() {
128+
if (AopUtils.isAopProxy(this.bean)) {
129+
Class<?> target = AopProxyUtils.ultimateTargetClass(this.bean);
130+
return AopUtils.getMostSpecificMethod(getMethod(), target);
131+
}
132+
else {
133+
return getMethod();
134+
}
135+
}
136+
124137
@Override
125138
protected StringBuilder getEndpointDescription() {
126139
return super.getEndpointDescription()

spring-jms/src/test/java/org/springframework/jms/annotation/JmsListenerAnnotationBeanPostProcessorTests.java

Lines changed: 94 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,13 @@
2020
import java.lang.annotation.Retention;
2121
import java.lang.annotation.RetentionPolicy;
2222
import java.lang.annotation.Target;
23+
import java.lang.reflect.Method;
2324

25+
import org.junit.Rule;
2426
import org.junit.Test;
27+
import org.junit.rules.ExpectedException;
2528

29+
import org.springframework.beans.factory.BeanCreationException;
2630
import org.springframework.context.ConfigurableApplicationContext;
2731
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
2832
import org.springframework.context.annotation.Bean;
@@ -34,16 +38,26 @@
3438
import org.springframework.jms.config.MessageListenerTestContainer;
3539
import org.springframework.jms.config.MethodJmsListenerEndpoint;
3640
import org.springframework.jms.listener.SimpleMessageListenerContainer;
41+
import org.springframework.messaging.handler.annotation.SendTo;
3742
import org.springframework.stereotype.Component;
43+
import org.springframework.transaction.PlatformTransactionManager;
44+
import org.springframework.transaction.annotation.EnableTransactionManagement;
45+
import org.springframework.transaction.annotation.Transactional;
46+
import org.springframework.util.ReflectionUtils;
3847

48+
import static org.hamcrest.CoreMatchers.*;
3949
import static org.junit.Assert.*;
50+
import static org.mockito.Mockito.*;
4051

4152
/**
4253
* @author Stephane Nicoll
4354
* @author Juergen Hoeller
4455
*/
4556
public class JmsListenerAnnotationBeanPostProcessorTests {
4657

58+
@Rule
59+
public final ExpectedException thrown = ExpectedException.none();
60+
4761
@Test
4862
public void simpleMessageListener() {
4963
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
@@ -73,10 +87,42 @@ public void metaAnnotationIsDiscovered() {
7387
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
7488
Config.class, MetaAnnotationTestBean.class);
7589

76-
JmsListenerContainerTestFactory factory = context.getBean(JmsListenerContainerTestFactory.class);
77-
assertEquals("one container should have been registered", 1, factory.getListenerContainers().size());
78-
JmsListenerEndpoint endpoint = factory.getListenerContainers().get(0).getEndpoint();
79-
assertEquals("metaTestQueue", ((AbstractJmsListenerEndpoint) endpoint).getDestination());
90+
try {
91+
JmsListenerContainerTestFactory factory = context.getBean(JmsListenerContainerTestFactory.class);
92+
assertEquals("one container should have been registered", 1, factory.getListenerContainers().size());
93+
JmsListenerEndpoint endpoint = factory.getListenerContainers().get(0).getEndpoint();
94+
assertEquals("metaTestQueue", ((AbstractJmsListenerEndpoint) endpoint).getDestination());
95+
}
96+
finally {
97+
context.close();
98+
}
99+
}
100+
101+
@Test
102+
public void sendToAnnotationFoundOnProxy() {
103+
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
104+
Config.class, ProxyConfig.class, ProxyTestBean.class);
105+
try {
106+
JmsListenerContainerTestFactory factory = context.getBean(JmsListenerContainerTestFactory.class);
107+
assertEquals("one container should have been registered", 1, factory.getListenerContainers().size());
108+
JmsListenerEndpoint endpoint = factory.getListenerContainers().get(0).getEndpoint();
109+
Method m = ReflectionUtils.findMethod(endpoint.getClass(), "getDefaultResponseDestination");
110+
ReflectionUtils.makeAccessible(m);
111+
Object destination = ReflectionUtils.invokeMethod(m, endpoint);
112+
assertEquals("SendTo annotation not found on proxy", "foobar", destination);
113+
}
114+
finally {
115+
context.close();
116+
}
117+
}
118+
119+
@Test
120+
public void invalidProxy() {
121+
thrown.expect(BeanCreationException.class);
122+
thrown.expectCause(is(instanceOf(IllegalStateException.class)));
123+
thrown.expectMessage("handleIt2");
124+
new AnnotationConfigApplicationContext(
125+
Config.class, ProxyConfig.class, InvalidProxyTestBean.class);
80126
}
81127

82128

@@ -102,7 +148,7 @@ public void handleIt(String body) {
102148
@JmsListener(destination = "metaTestQueue")
103149
@Target(ElementType.METHOD)
104150
@Retention(RetentionPolicy.RUNTIME)
105-
static @interface FooListener {
151+
@interface FooListener {
106152
}
107153

108154

@@ -128,4 +174,47 @@ public JmsListenerContainerTestFactory testFactory() {
128174
}
129175
}
130176

177+
@Configuration
178+
@EnableTransactionManagement
179+
static class ProxyConfig {
180+
181+
@Bean
182+
public PlatformTransactionManager transactionManager() {
183+
return mock(PlatformTransactionManager.class);
184+
}
185+
186+
}
187+
188+
interface SimpleService {
189+
190+
void handleIt(String body);
191+
192+
}
193+
194+
@Component
195+
static class ProxyTestBean implements SimpleService {
196+
197+
@Override
198+
@Transactional
199+
@JmsListener(destination = "testQueue")
200+
@SendTo("foobar")
201+
public void handleIt(String body) {
202+
203+
}
204+
}
205+
206+
@Component
207+
static class InvalidProxyTestBean implements SimpleService {
208+
209+
@Override
210+
public void handleIt(String body) {
211+
}
212+
213+
@Transactional
214+
@JmsListener(destination = "testQueue")
215+
@SendTo("foobar")
216+
public void handleIt2(String body) {
217+
}
218+
}
219+
131220
}

0 commit comments

Comments
 (0)