From 24e8048e70109347cb3badfb2da76f7cc634e574 Mon Sep 17 00:00:00 2001 From: Artem Bilan Date: Wed, 10 Apr 2019 15:09:26 -0400 Subject: [PATCH] GH-973: Higher order for RabbitListTestBootstrap (#976) * GH-973: Higher order for RabbitListTestBootstrap Fixes https://github.com/spring-projects/spring-amqp/issues/973 When we use `@EnableRabbit` and `@RabbitListenerTest` in the same configuration set, e.g. mixing real `@Configuration` and test one for `@RabbitListenerTest`, we may end up with the case when `@EnableRabbit` is processed before `@RabbitListenerTest`, so, `RabbitListenerTestHarness` bean is not going to appear in the application context. * Implement a `DeferredImportSelector` with an `@Order` for the `@EnableRabbit` as well as `RabbitListenerTest` giving higher order to the `RabbitListenerTestSelector`, so `RabbitListenerTestBootstrap` is processed and register its `RabbitListenerTestHarness` earlier, than it is done by the `RabbitBootstrapConfiguration` **Cherry-pick to 2.1.x** * * Fix Checkstyle * Add JavaDocs to new classes --- .../amqp/rabbit/test/RabbitListenerTest.java | 4 +- .../test/RabbitListenerTestBootstrap.java | 27 +++++------- .../test/RabbitListenerTestHarness.java | 5 --- .../test/RabbitListenerTestSelector.java | 42 +++++++++++++++++++ .../amqp/rabbit/annotation/EnableRabbit.java | 14 +++++-- .../RabbitListenerConfigurationSelector.java | 39 +++++++++++++++++ 6 files changed, 105 insertions(+), 26 deletions(-) create mode 100644 spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTestSelector.java create mode 100644 spring-rabbit/src/main/java/org/springframework/amqp/rabbit/annotation/RabbitListenerConfigurationSelector.java diff --git a/spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTest.java b/spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTest.java index bdadc43a..464d8f16 100644 --- a/spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTest.java +++ b/spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTest.java @@ -30,6 +30,8 @@ import org.springframework.context.annotation.Import; * {@code @RabbitListener} beans to capture arguments and result (if any). * * @author Gary Russell + * @author Artem Bilan + * * @since 1.6 * */ @@ -37,7 +39,7 @@ import org.springframework.context.annotation.Import; @Retention(RetentionPolicy.RUNTIME) @Documented @EnableRabbit -@Import(RabbitListenerTestBootstrap.class) +@Import(RabbitListenerTestSelector.class) public @interface RabbitListenerTest { /** diff --git a/spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTestBootstrap.java b/spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTestBootstrap.java index 4ff42251..6b1a306d 100644 --- a/spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTestBootstrap.java +++ b/spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTestBootstrap.java @@ -16,36 +16,31 @@ package org.springframework.amqp.rabbit.test; -import org.springframework.amqp.rabbit.annotation.RabbitListenerAnnotationBeanPostProcessor; import org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils; -import org.springframework.beans.factory.config.BeanDefinition; -import org.springframework.context.annotation.Bean; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.ImportAware; -import org.springframework.context.annotation.Role; +import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; /** * Overrides the default BPP with a {@link RabbitListenerTestHarness}. * * @author Gary Russell + * @author Artem Bilan + * * @since 1.6 * */ @Configuration -public class RabbitListenerTestBootstrap implements ImportAware { - - private AnnotationMetadata importMetadata; +public class RabbitListenerTestBootstrap implements ImportBeanDefinitionRegistrar { @Override - public void setImportMetadata(AnnotationMetadata importMetadata) { - this.importMetadata = importMetadata; - } - - @Bean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME) - @Role(BeanDefinition.ROLE_INFRASTRUCTURE) - public RabbitListenerAnnotationBeanPostProcessor rabbitListenerAnnotationProcessor() { - return new RabbitListenerTestHarness(this.importMetadata); + public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { + registry.registerBeanDefinition(RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME, + BeanDefinitionBuilder.rootBeanDefinition(RabbitListenerTestHarness.class) + .addConstructorArgValue(importingClassMetadata) + .getBeanDefinition()); } } diff --git a/spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTestHarness.java b/spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTestHarness.java index 841820f9..438fe0d7 100644 --- a/spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTestHarness.java +++ b/spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTestHarness.java @@ -114,11 +114,6 @@ public class RabbitListenerTestHarness extends RabbitListenerAnnotationBeanPostP return (T) this.listeners.get(id); } - @Override - public int getOrder() { - return super.getOrder() - 100; - } - private static final class CaptureAdvice implements MethodInterceptor { private final BlockingQueue invocationData = new LinkedBlockingQueue<>(); diff --git a/spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTestSelector.java b/spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTestSelector.java new file mode 100644 index 00000000..7e5360cc --- /dev/null +++ b/spring-rabbit-test/src/main/java/org/springframework/amqp/rabbit/test/RabbitListenerTestSelector.java @@ -0,0 +1,42 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.amqp.rabbit.test; + +import org.springframework.amqp.rabbit.annotation.RabbitListenerConfigurationSelector; +import org.springframework.core.Ordered; +import org.springframework.core.annotation.Order; +import org.springframework.core.type.AnnotationMetadata; + +/** + * A {@link RabbitListenerConfigurationSelector} extension to register + * a {@link RabbitListenerTestBootstrap}, but already with the higher order, + * so the {@link RabbitListenerTestHarness} bean is registered earlier, + * than {@link org.springframework.amqp.rabbit.annotation.RabbitListenerAnnotationBeanPostProcessor}. + * + * @author Artem Bilan + * + * @since 2.1.6 + */ +@Order(Ordered.LOWEST_PRECEDENCE - 100) +public class RabbitListenerTestSelector extends RabbitListenerConfigurationSelector { + + @Override + public String[] selectImports(AnnotationMetadata importingClassMetadata) { + return new String[] { RabbitListenerTestBootstrap.class.getName() }; + } + +} diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/annotation/EnableRabbit.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/annotation/EnableRabbit.java index 9bdc95e5..f7724f48 100644 --- a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/annotation/EnableRabbit.java +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/annotation/EnableRabbit.java @@ -47,7 +47,8 @@ import org.springframework.context.annotation.Import; * * The {@code RabbitListenerContainerFactory} is responsible to create the listener container * responsible for a particular endpoint. Typical implementations, as the - * {@link org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory SimpleRabbitListenerContainerFactory} + * {@link org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory + * SimpleRabbitListenerContainerFactory} * used in the sample above, provides the necessary configuration options that are supported by * the underlying {@link org.springframework.amqp.rabbit.listener.MessageListenerContainer MessageListenerContainer}. * @@ -111,9 +112,11 @@ import org.springframework.context.annotation.Import; * // process incoming message * } * - * These features are abstracted by the {@link org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory + * These features are abstracted by the + * {@link org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory * MessageHandlerMethodFactory} that is responsible to build the necessary invoker to process - * the annotated method. By default, {@link org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory + * the annotated method. By default, + * {@link org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory * DefaultMessageHandlerMethodFactory} is used. * *

When more control is desired, a {@code @Configuration} class may implement @@ -249,7 +252,10 @@ import org.springframework.context.annotation.Import; * definition registered in the context in case you use the XML configuration. * * @author Stephane Nicoll + * @author Artem Bilan + * * @since 1.4 + * * @see RabbitListener * @see RabbitListenerAnnotationBeanPostProcessor * @see org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar @@ -258,6 +264,6 @@ import org.springframework.context.annotation.Import; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented -@Import(RabbitBootstrapConfiguration.class) +@Import(RabbitListenerConfigurationSelector.class) public @interface EnableRabbit { } diff --git a/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/annotation/RabbitListenerConfigurationSelector.java b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/annotation/RabbitListenerConfigurationSelector.java new file mode 100644 index 00000000..b753885b --- /dev/null +++ b/spring-rabbit/src/main/java/org/springframework/amqp/rabbit/annotation/RabbitListenerConfigurationSelector.java @@ -0,0 +1,39 @@ +/* + * Copyright 2019 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.amqp.rabbit.annotation; + +import org.springframework.context.annotation.DeferredImportSelector; +import org.springframework.core.annotation.Order; +import org.springframework.core.type.AnnotationMetadata; + +/** + * A {@link DeferredImportSelector} implementation with the lowest order to import a + * {@link RabbitBootstrapConfiguration} as late as possible. + * + * @author Artem Bilan + * + * @since 2.1.6 + */ +@Order +public class RabbitListenerConfigurationSelector implements DeferredImportSelector { + + @Override + public String[] selectImports(AnnotationMetadata importingClassMetadata) { + return new String[] { RabbitBootstrapConfiguration.class.getName() }; + } + +}