From 3793fc1d612cccf1d2f77a14dd6cd26c960b6294 Mon Sep 17 00:00:00 2001 From: Gary Russell Date: Thu, 2 Jan 2020 10:21:07 -0500 Subject: [PATCH] GH-1136: Support Meta Annotations with AliasFor Resolves https://github.com/spring-projects/spring-amqp/issues/1136 Note that this is a minimal implementation that avoids major refactoring of the BPP. Repeatable user meta-annotatiions are not supported. In a future release we should perform that refactoring as well as supporting `@Repeatable` user annotations. Also restore the `stop()` with finally to fix `EnableRabbitTests.testProperShutdownOnException()` which took 30 seconds to run after the finally was removed by c1e31798961262795c4aa2b4bab3f0f67fedda26 * Increase search scope --- ...itListenerAnnotationBeanPostProcessor.java | 26 ++++++++++++++++++- .../AbstractMessageListenerContainer.java | 10 +++++++ ...tenerAnnotationBeanPostProcessorTests.java | 14 +++++++--- 3 files changed, 46 insertions(+), 4 deletions(-) diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/annotation/RabbitListenerAnnotationBeanPostProcessor.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/annotation/RabbitListenerAnnotationBeanPostProcessor.java index 763219ae35..ff19563f61 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/annotation/RabbitListenerAnnotationBeanPostProcessor.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/annotation/RabbitListenerAnnotationBeanPostProcessor.java @@ -69,6 +69,9 @@ import org.springframework.context.expression.StandardBeanExpressionResolver; import org.springframework.core.Ordered; import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.core.annotation.MergedAnnotations.SearchStrategy; import org.springframework.core.convert.ConversionService; import org.springframework.core.convert.converter.Converter; import org.springframework.core.convert.support.DefaultConversionService; @@ -421,9 +424,30 @@ private Method checkProxy(Method methodArg, Object bean) { return method; } - protected void processListener(MethodRabbitListenerEndpoint endpoint, RabbitListener rabbitListener, Object bean, + protected void processListener(MethodRabbitListenerEndpoint endpoint, RabbitListener rabbitListenerArg, Object bean, Object target, String beanName) { + RabbitListener rabbitListener = rabbitListenerArg; + MergedAnnotation mergedAnnotation = MergedAnnotation.missing(); + /* + * Synthesize the actual annotation to handle meta-annotations and aliasing. Note + * that only single @RabbitListener annotations can be meta-annotated. + */ + if (endpoint instanceof MultiMethodRabbitListenerEndpoint) { + if (AnnotationUtils.findAnnotation((Class) target, RabbitListeners.class) == null) { + mergedAnnotation = MergedAnnotations.from((Class) target, SearchStrategy.TYPE_HIERARCHY) + .get(RabbitListener.class); + } + } + else { + if (AnnotationUtils.findAnnotation(endpoint.getMethod(), RabbitListeners.class) == null) { + mergedAnnotation = MergedAnnotations.from(endpoint.getMethod(), SearchStrategy.TYPE_HIERARCHY) + .get(RabbitListener.class); + } + } + if (!MergedAnnotation.missing().equals(mergedAnnotation)) { + rabbitListener = mergedAnnotation.synthesize(); + } endpoint.setBean(bean); endpoint.setMessageHandlerMethodFactory(this.messageHandlerMethodFactory); endpoint.setId(getEndpointId(rabbitListener)); diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java index b70392806e..3d14b72d95 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/listener/AbstractMessageListenerContainer.java @@ -1341,6 +1341,16 @@ public void stop() { } } + @Override + public void stop(Runnable callback) { + try { + stop(); + } + finally { + callback.run(); + } + } + /** * This method is invoked when the container is stopping. */ diff --git a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/annotation/RabbitListenerAnnotationBeanPostProcessorTests.java b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/annotation/RabbitListenerAnnotationBeanPostProcessorTests.java index 1f248c02df..f945353184 100644 --- a/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/annotation/RabbitListenerAnnotationBeanPostProcessorTests.java +++ b/spring-rabbit/src/test/java/org/springframework/amqp/rabbit/annotation/RabbitListenerAnnotationBeanPostProcessorTests.java @@ -55,6 +55,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; +import org.springframework.core.annotation.AliasFor; import org.springframework.stereotype.Component; /** @@ -124,7 +125,10 @@ public void metaAnnotationIsDiscovered() { RabbitListenerContainerTestFactory factory = context.getBean(RabbitListenerContainerTestFactory.class); assertThat(factory.getListenerContainers().size()).as("one container should have been registered").isEqualTo(1); RabbitListenerEndpoint endpoint = factory.getListenerContainers().get(0).getEndpoint(); - assertThat(((AbstractRabbitListenerEndpoint) endpoint).getQueueNames().iterator().next()).isEqualTo("metaTestQueue"); + assertThat(((AbstractRabbitListenerEndpoint) endpoint).getQueueNames() + .iterator() + .next()) + .isEqualTo("metaTestQueue"); context.close(); } @@ -330,16 +334,20 @@ public void handleIt(String body) { @Component static class MetaAnnotationTestBean { - @FooListener + @FooListener("metaTestQueue") public void handleIt(String body) { } } - @RabbitListener(queues = "metaTestQueue") + @RabbitListener @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) static @interface FooListener { + + @AliasFor(annotation = RabbitListener.class, attribute = "queues") + String[] value() default {}; + } @Component