Spring-boot: Override RabbitListenerAnnotationBeanPostProcessor default bean doesn't work in spring-boot v2.1.4.RELEASE

Created on 24 May 2019  路  6Comments  路  Source: spring-projects/spring-boot

Good night.

I have a library that extends Spring AMQP and changes the default Autoconfiguration file (I will open source it).

But when I migrate to Spring Boot 2.1.4, the method that is responsible to change the RabbitListenerAnnotationBeanPostProcessor behavior simply stop to be called.

@Primary
@Bean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_APPLICATION)
@DependsOn(RabbitConstants.CONNECTION_FACTORY_BEAN_NAME)
public RabbitListenerAnnotationBeanPostProcessor rabbitListenerAnnotationProcessor() {
    return new NewRabbitListenerAnnotationBeanPostProcessor();
}

This method belongs to a class that I defined to be the default Autoconfiguration file.

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.amqp.NewRabbitAutoConfiguration
@Configuration
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@EnableConfigurationProperties({ RabbitProperties.class, RabbitCustomPropertiesMap.class })
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Import(RabbitAnnotationDrivenConfiguration.class)
public class NewRabbitAutoConfiguration {
...
}

So, if I can't call my NewRabbitListenerAnnotationBeanPostProcessor I will be a problem because I have some treats for the attribute containerFactory of RabbitListener annotation.


UPDATE 1

I think the problem is: RabbitListenerAnnotationBeanPostProcessor is executing before the new AutoConfiguration class (NewRabbitAutoConfiguration)

UPDATE 2

I tried to annotate the bean of my Listener with @ConditionalOnBean(NewRabbitAutoConfiguration.class), but the result was very strange, Spring called all methods of the NewRabbitAutoConfiguration but not call the rabbitListenerAnnotationProcessor method (and the bean RabbitConstants.CONNECTION_FACTORY_BEAN_NAME) was created.

UPDATE 3

I've created a repo to simulate the problem: https://github.com/juliofalbo/spring-2.1.4-bug-override-bean-post-processor

Note: If we change the version of Spring Boot to 2.1.3.RELEASE, works fine!


Can someone help me with this issue?

invalid

Most helpful comment

I'm not sure what exactly has changed between 2.1.3 and 2.1.4 but your configuration looks quite unusual and I wouldn't recommend taking such an approach.

Specifically:

  • Trying to directly import auto-configuration delegate classes is quite brittle
  • You should not use a package named org.springframework.boot.autoconfigure.amqp in an open source project. The org.springframework package is reserved for official Spring projects.
  • Requiring users to have spring.main.allow-bean-definition-overriding=true is not recommended
  • Having @AutoConfigureOrder of HIGHEST is likely going to cause problems
  • Your rabbitListenerAnnotationProcessor method might cause some initialization issues since it's a bean post processor that depends on a connection factory.

Instead I think it might be better to structure your configuration like this:

@Configuration
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@AutoConfigureBefore(RabbitAutoConfiguration.class)
@Import({NewRabbitAutoConfiguration.RabbitPostProcessorConfiguration.class, RabbitAnnotationDrivenConfiguration.EnableRabbitConfiguration.class})
public class NewRabbitAutoConfiguration {

    @Configuration
    static class RabbitPostProcessorConfiguration {

        @Bean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
        public static RabbitListenerAnnotationBeanPostProcessor rabbitListenerAnnotationProcessor() {
            return new NewRabbitListenerAnnotationBeanPostProcessor();
        }

    }

    @Configuration
    @EnableRabbit
    static class EnableRabbitConfiguration {

        @Bean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
        public static RabbitListenerAnnotationBeanPostProcessor rabbitListenerAnnotationProcessor() {
            return new NewRabbitListenerAnnotationBeanPostProcessor();
        }

    }
}

This will create a configuration that's ordered to run before ours (because of @AutoConfigureBefore(RabbitAutoConfiguration.class)). It will import a couple of configuration in a specific order. The first will register your post processor and the second will enable rabbit.

The @EnableRabbit annotation triggers RabbitBootstrapConfiguration but this won't replace your post processor because it has an explicit check. When our auto-configuration runs it backs-off because it already finds a post processor.

All 6 comments

I'm not sure what exactly has changed between 2.1.3 and 2.1.4 but your configuration looks quite unusual and I wouldn't recommend taking such an approach.

Specifically:

  • Trying to directly import auto-configuration delegate classes is quite brittle
  • You should not use a package named org.springframework.boot.autoconfigure.amqp in an open source project. The org.springframework package is reserved for official Spring projects.
  • Requiring users to have spring.main.allow-bean-definition-overriding=true is not recommended
  • Having @AutoConfigureOrder of HIGHEST is likely going to cause problems
  • Your rabbitListenerAnnotationProcessor method might cause some initialization issues since it's a bean post processor that depends on a connection factory.

Instead I think it might be better to structure your configuration like this:

@Configuration
@ConditionalOnClass({ RabbitTemplate.class, Channel.class })
@AutoConfigureBefore(RabbitAutoConfiguration.class)
@Import({NewRabbitAutoConfiguration.RabbitPostProcessorConfiguration.class, RabbitAnnotationDrivenConfiguration.EnableRabbitConfiguration.class})
public class NewRabbitAutoConfiguration {

    @Configuration
    static class RabbitPostProcessorConfiguration {

        @Bean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
        public static RabbitListenerAnnotationBeanPostProcessor rabbitListenerAnnotationProcessor() {
            return new NewRabbitListenerAnnotationBeanPostProcessor();
        }

    }

    @Configuration
    @EnableRabbit
    static class EnableRabbitConfiguration {

        @Bean(name = RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)
        public static RabbitListenerAnnotationBeanPostProcessor rabbitListenerAnnotationProcessor() {
            return new NewRabbitListenerAnnotationBeanPostProcessor();
        }

    }
}

This will create a configuration that's ordered to run before ours (because of @AutoConfigureBefore(RabbitAutoConfiguration.class)). It will import a couple of configuration in a specific order. The first will register your post processor and the second will enable rabbit.

The @EnableRabbit annotation triggers RabbitBootstrapConfiguration but this won't replace your post processor because it has an explicit check. When our auto-configuration runs it backs-off because it already finds a post processor.

Hi Phil!
Thank you very much for your quick help!

Perfect, I'm sorry about the package, I didn't know about this info, but one question.
How can I access the RabbitAnnotationDrivenConfiguration.class in another package? This class is not public. :(

I don't think you need to so do. Phil is proposing that you have your own EnableRabbitConfiguration so you can drop the import of RabbitAnnotationDrivenConfiguration.EnableRabbitConfiguration.class.

Perfect Andy!

Thank you very much everyone!

Hi, I have another question.

Phil said that requiring users to have spring.main.allow-bean-definition-overriding=true is not recommended, and I agree 100%, but there is another way to avoid this?

Thank you very much all support!

@juliofalbo I don't think it's needed with the sample I gave above because rather than replacing beans, you version registers them first and the other configurations just back off.

Was this page helpful?
0 / 5 - 0 ratings