提交 adc7ad7f 编写于 作者: S Stephane Nicoll

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
上级 b796c1e8
...@@ -19,6 +19,8 @@ package org.springframework.jms.config; ...@@ -19,6 +19,8 @@ package org.springframework.jms.config;
import java.lang.reflect.Method; import java.lang.reflect.Method;
import java.util.Arrays; 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.core.annotation.AnnotationUtils;
import org.springframework.jms.listener.MessageListenerContainer; import org.springframework.jms.listener.MessageListenerContainer;
import org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter; import org.springframework.jms.listener.adapter.MessagingMessageListenerAdapter;
...@@ -109,18 +111,29 @@ public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint { ...@@ -109,18 +111,29 @@ public class MethodJmsListenerEndpoint extends AbstractJmsListenerEndpoint {
} }
private String getDefaultResponseDestination() { private String getDefaultResponseDestination() {
SendTo ann = AnnotationUtils.getAnnotation(getMethod(), SendTo.class); Method specificMethod = getMostSpecificMethod();
SendTo ann = AnnotationUtils.getAnnotation(specificMethod, SendTo.class);
if (ann != null) { if (ann != null) {
Object[] destinations = ann.value(); Object[] destinations = ann.value();
if (destinations.length != 1) { if (destinations.length != 1) {
throw new IllegalStateException("Invalid @" + SendTo.class.getSimpleName() + " annotation on '" 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 (String) destinations[0];
} }
return null; 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 @Override
protected StringBuilder getEndpointDescription() { protected StringBuilder getEndpointDescription() {
return super.getEndpointDescription() return super.getEndpointDescription()
......
...@@ -20,9 +20,13 @@ import java.lang.annotation.ElementType; ...@@ -20,9 +20,13 @@ import java.lang.annotation.ElementType;
import java.lang.annotation.Retention; import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy; import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target; import java.lang.annotation.Target;
import java.lang.reflect.Method;
import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.beans.factory.BeanCreationException;
import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
...@@ -34,9 +38,16 @@ import org.springframework.jms.config.JmsListenerEndpointRegistry; ...@@ -34,9 +38,16 @@ import org.springframework.jms.config.JmsListenerEndpointRegistry;
import org.springframework.jms.config.MessageListenerTestContainer; import org.springframework.jms.config.MessageListenerTestContainer;
import org.springframework.jms.config.MethodJmsListenerEndpoint; import org.springframework.jms.config.MethodJmsListenerEndpoint;
import org.springframework.jms.listener.SimpleMessageListenerContainer; import org.springframework.jms.listener.SimpleMessageListenerContainer;
import org.springframework.messaging.handler.annotation.SendTo;
import org.springframework.stereotype.Component; 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.junit.Assert.*;
import static org.mockito.Mockito.*;
/** /**
* @author Stephane Nicoll * @author Stephane Nicoll
...@@ -44,6 +55,9 @@ import static org.junit.Assert.*; ...@@ -44,6 +55,9 @@ import static org.junit.Assert.*;
*/ */
public class JmsListenerAnnotationBeanPostProcessorTests { public class JmsListenerAnnotationBeanPostProcessorTests {
@Rule
public final ExpectedException thrown = ExpectedException.none();
@Test @Test
public void simpleMessageListener() { public void simpleMessageListener() {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
...@@ -73,10 +87,42 @@ public class JmsListenerAnnotationBeanPostProcessorTests { ...@@ -73,10 +87,42 @@ public class JmsListenerAnnotationBeanPostProcessorTests {
ConfigurableApplicationContext context = new AnnotationConfigApplicationContext( ConfigurableApplicationContext context = new AnnotationConfigApplicationContext(
Config.class, MetaAnnotationTestBean.class); Config.class, MetaAnnotationTestBean.class);
JmsListenerContainerTestFactory factory = context.getBean(JmsListenerContainerTestFactory.class); try {
assertEquals("one container should have been registered", 1, factory.getListenerContainers().size()); JmsListenerContainerTestFactory factory = context.getBean(JmsListenerContainerTestFactory.class);
JmsListenerEndpoint endpoint = factory.getListenerContainers().get(0).getEndpoint(); assertEquals("one container should have been registered", 1, factory.getListenerContainers().size());
assertEquals("metaTestQueue", ((AbstractJmsListenerEndpoint) endpoint).getDestination()); 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 { ...@@ -102,7 +148,7 @@ public class JmsListenerAnnotationBeanPostProcessorTests {
@JmsListener(destination = "metaTestQueue") @JmsListener(destination = "metaTestQueue")
@Target(ElementType.METHOD) @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME) @Retention(RetentionPolicy.RUNTIME)
static @interface FooListener { @interface FooListener {
} }
...@@ -128,4 +174,47 @@ public class JmsListenerAnnotationBeanPostProcessorTests { ...@@ -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) {
}
}
} }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册