Inheritance with SqsListener

492 Views Asked by At

I have a strange situation that I don't understand. I would like to decide in a bean, should I enable or disable the SQS listener. So I've created a config class with a definition:

@Bean
MyListener createListener(Features f){
  return f.shouldListen() ? new RealListener() : new MockListener();
}

As you can see, I have such an inheritance:

interface MyListener{}
class RealListener implements MyListener{
  @SqsListener(...)
  list handleMessage(SqsMessage message){
    ...
  }
}
class MockListener implements MyListener{}

Now, the funny part:

It sometimes works.

After few restarts of the application, the handleMessage() method is called but in most cases it isn't without any exception. The queue is created, all permissions are in place. To make it working I need to return RealListener from createListener() method or move @SqsListener annotation to a method in the MyListener interface. Both are not options for me, because I don't want to call AWS, when the mock is enabled.

I've tried with conditional bean creation but as Features depends on a DB (indirect entityManager dependency, to be more precise), I can't make it working. I've tried with abstract class instead of interface, but without luck. I've tried to register the RealListener bean in the BeanFactoryPostProcessor but this also doesn't work (same entityManager dependency issue). I've tried to move the annotation to the interface and use @ConditionalOnBean with @Primary to create an empty AmazonSqsClient when the mock is enabled, but it doesn't work.

I could understand that it doesn't work, because the bean has to be created for a type with the method annotated with @SqsListener (not with it's supperclass/interface type), but I have three such beans and a lottery - sometimes all work, sometimes one or two but sometimes none of those.

Do you have any suggestions?

1

There are 1 best solutions below

0
On

Well... I've found the issue but it still would be nice, to know, what has happened.

So... There is a class QueueMessageHandler using detectHandlerMethods(...) method from the superclass AbstractMethodMessageHandler. This method uses MethodIntrospector.selectMethods() to select methods to scan. This class takes into consideration methods annotated with @SqsListener when there is @EnableSqs in some configuration class. The problem is, in my project the @EnableSqs annotation is located in some file - not the file with createListener(...) method and not the main class of the Spring Boot application. That means, the class with @EnableSqs can be loaded before or after MethodIntrospector.selectMethods().

The output is:

  1. I have no idea, why it works fine without inheritance and doesn't work with inheritance
  2. To fix the issue, I've moved @EnableSqs to the main class of the project