-
Notifications
You must be signed in to change notification settings - Fork 639
GH-891 Introduced MultiRabbit to handle multiple brokers #1111
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
@rwanderc Thanks! Will take a look ASAP. |
It will probably be next week before I can look at this in detail. We will need an update to the reference manual (src/reference/asciidoc) before we merge, but it can wait until after the review if you prefer. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good.
Some initial comments after a first pass review.
As previously mentioned; some doc updates would be good - particularly with respect to usage.
Perhaps an integration test or two (e.g. using 2 connection factories, which can be mocks) would be useful.
/** | ||
* An {@link ImportBeanDefinitionRegistrar} class that registers a | ||
* {@link MultiRabbitListenerAnnotationBeanPostProcessor} (overwriting the regular | ||
* {@link RabbitListenerAnnotationBeanPostProcessor}) bean capable of processing Spring's @{@link RabbitListener} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We limit javadocs to 90 chars (and code to 120).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please, stop sending Done
and Fixed
message.
We definitely get a notification about new commits in the PR.
Me personally follow the rule: if I don't agree or have some doubts, I comment on the review. Otherwise I just push the fix silently.
It really has just sounded as a spam on my phone 😄
Thanks for understanding
|
||
private MultiRabbitConstants() { | ||
throw new UnsupportedOperationException("Construction not supported."); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tabs only, please, and a blank line between fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.
* @see RabbitListenerAnnotationBeanPostProcessor | ||
*/ | ||
public class MultiRabbitListenerAnnotationBeanPostProcessor extends RabbitListenerAnnotationBeanPostProcessor | ||
implements ApplicationContextAware, BeanFactoryAware { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Redundant interface BFA
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed.
implements ApplicationContextAware, BeanFactoryAware { | ||
|
||
private ApplicationContext applicationContext; | ||
private BeanFactory beanFactory; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
blank line between fields.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
private void enhanceBeansWithReferenceToRabbitAdmin(RabbitListener rabbitListener) { | ||
RabbitAdmin rabbitAdmin = getRabbitAdminBean(rabbitListener); | ||
// Enhance Exchanges | ||
this.applicationContext.getBeansOfType(AbstractExchange.class).values().stream().filter(this::isNotProcessed) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use getBeansOfType(clazz, false, false)
- we don't want to prematurely load factory beans.
However, getting beans at all in a BPP is not generally a good thing to do. Instead implement SmartInitializingSingleton
and defer these enhancements (e.g. store the RabbitAdmins
in a field and process them in afterSingletonsInstantiated()
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed to getBeansOfType(clazz, false, false)
.
Thanks for the advice.
About plugging in BPP, the reason is that BPP runs for each @RabbitListener
and creates the beans of the respective declarables (AbstractExchange
, Queue
and Binding
) during this process.
Therefore, plugging to BPP makes it possible to associate the proper RabbitAdmin
to the declarables just created. After they are enhanced with the RabbitAdmin
, they are filtered out from next @RabbitListener
processed (that's the reason of method isNotProcessed()
).
Indeed the complexity of each of these statements is very bad (O(n2)
for n
@RabbitListeners
), however I could not find a better solution to automate this process without the user's input (e.g. defining the RabbitAdmin
's bean name in each declarable).
As far as I understand, implementing SmartInitializingSingleton
will provide a processor after all beans are instantiated, which is much better in terms of complexity. But how could I know which declarable relates to each RabbitAdmin
to make the association?
RabbitAdmin rabbitAdmin = getRabbitAdminBean(rabbitListener); | ||
// Enhance Exchanges | ||
this.applicationContext.getBeansOfType(AbstractExchange.class).values().stream().filter(this::isNotProcessed) | ||
.forEach(exchange -> exchange.setAdminsThatShouldDeclare(rabbitAdmin)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we really want to declare all exchanges on all servers?
Perhaps we could use some naming convention to determine which admin(s) should declare which declarables?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. The declarables (AbstractExchange
, Queue
and Binding
) are not declared on all servers.
That's the reason for the isNotProcessed()
method.
Once the declarable beans are created by BPP for one @RabbitListener
, they will not have any RabbitAdmin
yet associated.
Then, they are enhanced with the corresponding RabbitAdmin
.
Then, when BPP processes the 2nd, 3rd, etc @RabbitListener
s, the previous declarables will be filtered out and not enhanced, because they already have a RabbitAdmin
associated to them.
This way, only the last declarables created are associated to the current RabbitAdmin
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh; now I see the reason for my confusion; this is a bit brittle - you are using the fact that the BPP "just" added the beans for the current @RabbitListener.queueBindings()
and the beans for other @QueueBinding
s don't exist yet.
The problem with this approach (using getBeansOfType()
) is that all stand-alone queue/exchange/binding @Bean
s will be declared by the first admin.
We could use the names from the @QueueBinding
s and do getBean()
for each individually to avoid picking up other unrelated beans.
However, this is complicated by the fact that the bean names are foo.n
(where n is incremented with a common integer) so there is no way to know which beans were registered for the current annotation.
I can see a couple of possible solutions -
- change
processAmqpListener()
to returnCollection<Declarable>
containing any beans that were registered for this listener in the super class - add a new
@RabbitListener
field toAbstractDeclarable
and use that as the.filter
.
*/ | ||
private boolean isNotProcessed(Declarable declarable) { | ||
return declarable.getDeclaringAdmins() == null || ( | ||
declarable.getDeclaringAdmins().stream().noneMatch(item -> item == this) && declarable |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
line break is a bit unusual
private boolean isNotProcessed(Declarable declarable) {
return declarable.getDeclaringAdmins() == null || (
declarable.getDeclaringAdmins().stream().noneMatch(item -> item == this)
&& declarable.getDeclaringAdmins().stream().noneMatch(item -> item instanceof RabbitAdmin));
}
That said, how can item == this
?
item
can be an admin instance or an admin bean name (see declarableByMe
in RabbitAdmin
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Indeed. Fixed line break.
The item == this
was a mistake. Thanks for the heads-up. Removed.
} | ||
|
||
@Override | ||
public void setApplicationContext(@NonNull ApplicationContext applicationContext) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Convention is to put accessors before all other methods.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
/** | ||
* @author Wander Costa | ||
*/ | ||
class MultiRabbitAdminNameResolverTest { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Convention is ...Tests
(even if only one).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Changed.
Instead of duplicating that test, I think you can simply subclass it and subclass |
Thank you so much @garyrussell for your comments. |
Sorry spamming you @garyrussell and @artembilan previously ... About the tests, I followed the advice on subclassing and providing an overloading, but it didn't replace the bean. I guess it's because the context is initialized always with a specific class About the integration tests, I created a couple of tests to make sure the declarables are declared by the proper |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding testing multiple CFs, you can either test with mocks or have multiple CFs using the same underlying connection factory. You can then spy()
the createConnection()
methods to verify the expected one was called. Also see the LocalizedQueueConnectionFactoryTests
.
RabbitAdmin rabbitAdmin = getRabbitAdminBean(rabbitListener); | ||
// Enhance Exchanges | ||
this.applicationContext.getBeansOfType(AbstractExchange.class).values().stream().filter(this::isNotProcessed) | ||
.forEach(exchange -> exchange.setAdminsThatShouldDeclare(rabbitAdmin)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh; now I see the reason for my confusion; this is a bit brittle - you are using the fact that the BPP "just" added the beans for the current @RabbitListener.queueBindings()
and the beans for other @QueueBinding
s don't exist yet.
The problem with this approach (using getBeansOfType()
) is that all stand-alone queue/exchange/binding @Bean
s will be declared by the first admin.
We could use the names from the @QueueBinding
s and do getBean()
for each individually to avoid picking up other unrelated beans.
However, this is complicated by the fact that the bean names are foo.n
(where n is incremented with a common integer) so there is no way to know which beans were registered for the current annotation.
I can see a couple of possible solutions -
- change
processAmqpListener()
to returnCollection<Declarable>
containing any beans that were registered for this listener in the super class - add a new
@RabbitListener
field toAbstractDeclarable
and use that as the.filter
.
Hi @garyrussell. As you recommended, I implemented the approach of returning the collection of |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I'll come back for more review when we switch a version for possible breaking changes.
Thank you for your effort so far!
* | ||
* @author Wander Costa | ||
*/ | ||
final class MultiRabbitAdminNameResolver { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure that we need this functionality as a top-level class at all.
Looks like that resolve()
method could be just private static
in the MultiRabbitListenerAnnotationBeanPostProcessor
* An {@link ImportBeanDefinitionRegistrar} class that registers a | ||
* {@link MultiRabbitListenerAnnotationBeanPostProcessor} (overwriting the regular | ||
* {@link RabbitListenerAnnotationBeanPostProcessor}) bean capable of processing | ||
* Spring's @{@link RabbitListener} annotations. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo: we don't need this extra @
before braces.
* | ||
* @author Wander Costa | ||
*/ | ||
public final class MultiRabbitConstants { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure in the name and purpose of this class.
Why all of these constants can't go into a MultiRabbitListenerAnnotationBeanPostProcessor
class?
Like we have already with its super one...
* configuration, preventing the server from automatic binding non-related structures. | ||
* | ||
* @author Wander Costa | ||
* @see RabbitListenerAnnotationBeanPostProcessor |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is no reason in @see
. You mention it in the JavaDocs and we have an extension from it anyway.
Object bean, String beanName) { | ||
final Collection<Declarable> declarables = super.processAmqpListener(rabbitListener, method, bean, beanName); | ||
final RabbitAdmin rabbitAdmin = resolveRabbitAdminBean(rabbitListener); | ||
declarables.stream() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would prefer regular loop with ifs instead of Stream
objects.
|
||
static final SimpleRoutingConnectionFactory ROUTING_CONNECTION_FACTORY = new SimpleRoutingConnectionFactory(); | ||
static final ConnectionFactory DEFAULT_CONNECTION_FACTORY = Mockito.mock(ConnectionFactory.class); | ||
static final ConnectionFactory CONNECTION_FACTORY_BROKER_B = Mockito.spy(ConnectionFactory.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo? There is nothing to spy
on the interface.
Also: doesn't look like an integration
test if you used mock.
Maybe better to call it MockMultiRabbitTests
?
Hi @garyrussell and @artembilan. First of all, thank you for the previous review you've done and comments. I worked on them as advised.
|
Thanks @rwanderc - we will try to review later this week. We have a big release day tomorrow (this will go into 2.3 later this year; there is no new Spring AMQP for this week's Boot 2.3). We'll build Milestone 1 soon after this is merged. Don't worry about squashing - we always do that before merging to master. Discrete commits makes it easier to review deltas during the review phase. |
Finally getting around to looking at this; sorry for the delay... |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good! Just some minor comments.
@rwanderc - I have scheduled the first milestone of 2.3 next Wednesday; it would be great to get this into that release (even without documentation).
If you don't think you can address these concerns by then, I can address them.
Thanks
@@ -0,0 +1,49 @@ | |||
/* | |||
* Copyright 2002-2020 the original author or authors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just 2020 for new files.
@@ -0,0 +1,99 @@ | |||
/* | |||
* Copyright 2002-2020 the original author or authors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just 2020.
final Collection<Declarable> declarables = super.processAmqpListener(rabbitListener, method, bean, beanName); | ||
final RabbitAdmin rabbitAdmin = resolveRabbitAdminBean(rabbitListener); | ||
for (final Declarable declarable : declarables) { | ||
if (declarable.getDeclaringAdmins().isEmpty() && declarable instanceof AbstractDeclarable) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can add setAdminsThatShouldDeclare
as a default
method on Declarable
and avoid the cast.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed according to the suggestion.
Also, I suppose you also mean removing the condition if (... && declarable instanceof AbstractDeclarable) {...}
to rely directly on the interface.
If that's the case, having a default
method on Declarable
might introduce space for a behavioral side-effect in MultiRabbit (declarables being processed by all admins, which I guess is not the main use-case). At spring-amqp
, I see no impact, since all concrete classes eventually also extend AbstractDeclarable
.
My thinking is that since these are breaking changes, why not introduce setAdminsThatShouldDeclare()
as a regular signature (non-default
) and force concrete classes to implement it?
Declarable
's concretes must also implement getDeclaringAdmins()
which makes me understand it's in the scope of its responsibilities.
What do you think about it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes; that would be ok (non-default) - I doubt users implement their own Declarable
s anyway.
*/ | ||
private RabbitAdmin resolveRabbitAdminBean(RabbitListener rabbitListener) { | ||
final String name = resolveAdminName(rabbitListener); | ||
return this.beanFactory.getBean(name, RabbitAdmin.class); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We shouldn't call getBean()
from a BPP - causes early instantiation.
That's why setAdminsThatShouldDeclare
takes Object...
- so we can just pass in the bean name.
Although I see that super.resolveAdmin()
does that too; I need to fix that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a big point! I'm changing it to provide only the name, and not prematurely instantiate the bean.
Also, regarding the logic here (and here), as I understand, it's intended to provide a fallback RabbitAdmin
name.
At this moment, super.processAmqpListener()
was already executed and resolved the name provided (if any) through the regular flow. Then, it's only important to assign a fallback bean name (the RabbitAdmin
generated from MultiRabbit) if none was specified.
Therefore, I guess resolveExpressionAsString()
wouldn't be necessary. Is that right?
Nevertheless, I provided it as suggested.
* @return The name of the RabbitAdmin bean. | ||
*/ | ||
public static String resolveAdminName(RabbitListener rabbitListener) { | ||
String admin = rabbitListener.admin(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to call resolveExpressionAsString()
- see super.resolveAdmin()
- we should make all those resolve*
methods protected.
@@ -0,0 +1,332 @@ | |||
/* | |||
* Copyright 2002-2020 the original author or authors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2020
@@ -0,0 +1,48 @@ | |||
/* | |||
* Copyright 2002-2019 the original author or authors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2020
@@ -0,0 +1,59 @@ | |||
/* | |||
* Copyright 2002-2019 the original author or authors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2020
@@ -0,0 +1,75 @@ | |||
/* | |||
* Copyright 2002-2020 the original author or authors. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2020
* @see RabbitListenerEndpointRegistry | ||
* @see EnableRabbit | ||
*/ | ||
public class MultiRabbitBootstrapConfiguration implements ImportBeanDefinitionRegistrar { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we need an annotation or DeferredImportSelector
to trigger this to ensure it's loaded before the RabbitBootstrapConfiguration
?
cc/ @artembilan
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can't the user just declare a bean with the subclass and RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME
since the RabbitListenerConfigurationSelector
won't run until all @Configuration
classes have been processed; removing the need for this class altogether.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, need to understand how it is tested over here though.
Probably just EnableRabbit(multi = true)
would be enough...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not too sure how to proceed here. No changes.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can just delete this; in order to override the default BPP, simply define the subclass as a bean with the default name, and the default one will be skipped here
Lines 51 to 56 in 715f39f
if (!registry.containsBeanDefinition( | |
RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)) { | |
registry.registerBeanDefinition(RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME, | |
new RootBeanDefinition(RabbitListenerAnnotationBeanPostProcessor.class)); | |
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi @garyrussell & @artembilan,
I'm writing this here to connect to the context of the issue.
I'm working on the boot part, and I tried to implement the instantiation of MultRabbitBPP the way suggested. But as overriding of beans is now disabled by default in Spring, it fails to register a bean with the same name as RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME
.
I'm trying to work around it by providing a MultiRabbit BPP bean with another name, via configuration. But when processing listeners, it still picks up the original one BPP and ALSO tries to process MultiRabbitBPP, leading to an error of multiple registrations of the same declarables.
Maybe it should never instantiate RabbitListenerAnnotationBeanPostProcessor
since MultiRabbitListenerAnnotationBeanPostProcessor
is provided, but I'm not figuring out how to make it work, or if that's related to some order of processing.
I got to make this work by directly implementing RabbitBootstrapConfiguration
to instantiate new RootBeanDefinition(MultiRabbitListenerAnnotationBeanPostProcessor.class)
whenever EnableRabbit(multi = true)
(as per @artembilan's suggestion), but I feel this is not the best solution (very intrusive and not SOLID).
Would you have any suggestions on how to proceed with this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another, perhaps less intrusive, option would be to add a new Boot property multiCluster
. While we can't reference the RabbitProperties
we can get a reference to the environment:
public class RabbitBootstrapConfiguration implements ImportBeanDefinitionRegistrar, EnvironmentAware {
private Environment environment;
@Override
public void registerBeanDefinitions(@Nullable AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
String multiS = this.environment.getProperty("spring.rabbitmq.multi-cluster");
boolean multi = multiS != null && Boolean.parseBoolean(multiS);
if (multi) {
...
}
else if (!registry.containsBeanDefinition(
RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME)) {
registry.registerBeanDefinition(RabbitListenerConfigUtils.RABBIT_LISTENER_ANNOTATION_PROCESSOR_BEAN_NAME,
new RootBeanDefinition(RabbitListenerAnnotationBeanPostProcessor.class));
}
if (!registry.containsBeanDefinition(RabbitListenerConfigUtils.RABBIT_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME)) {
registry.registerBeanDefinition(RabbitListenerConfigUtils.RABBIT_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME,
new RootBeanDefinition(RabbitListenerEndpointRegistry.class));
}
}
@Override
public void setEnvironment(Environment environment) {
this.environment = environment;
}
}
@garyrussell I will look into the comments tomorrow, or later on Sunday. If none of them require other rounds, my guess is that we get this into 2.3. :) Thanks |
…he bean name instead
@garyrussell & @artembilan, just (rebased and) pushed the changes for your recent comments. Pls let me know what you find, and I will of course jump in. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work; thanks for the contribution.
Next up: docs (if you have time - otherwise I'll take care of it).
Where can I find docs for this one? |
@KamilWitkowski7 Thanks for the ping, I had forgotten about this; and timely too, we are releasing 2.3.0 tomorrow. Pull request here: #1262 @rwanderc I would appreciate if you can give it a quick review, in case I missed anything. Thanks. |
Hi @garyrussell and Spring team, it took a long time but finally happened. =)
I have tried to make it simpler avoiding name conventions, but didn't manage to follow your advice. Maybe you could also give me some direction on what to change and how to really test it since the instances are provided in the
boot
.I created the test class
MultiRabbitListenerAnnotationBeanPostProcessorCompatibilityTests
as a copy ofRabbitListenerAnnotationBeanPostProcessorCompatibilityTests
to make sure nothing is broken from the default behavior if eventually the multirabbit BPP is loaded.Resolves #891