12

Problem: I am migrating from MessageListener interface impl to @RabbitListener. I had logic like this where I was doing "pre" and "post" message processing on a MessageListener that was inherited by several classes

example:

public AbstractMessageListener implements MessageListener {

     @Override
     public void onMessage(Message message) {

          //do some pre message processing

          process(Message message);

          // do some post message processing
     }

     protected abstract void process(Message message);

}

Question: Is there a way I can achieve something similar using @RabbitListener annotation Where I can inherit pre/post message processing logic without having to re-implement or call the pre/post message processing inside each child @RabbitListener annotation and all the while maintaining a customizable method signatures for the child @RabbitListener? Or is this being too greedy?

Example desired result:

public class SomeRabbitListenerClass {

    @RabbitListener( id = "listener.mypojo",queues = "${rabbitmq.some.queue}")
   public void listen(@Valid MyPojo myPojo) {
      //...
   }
}

public class SomeOtherRabbitListenerClass {

    @RabbitListener(id = "listener.orders",queues ="${rabbitmq.some.other.queue}")
   public void listen(Order order, @Header("order_type") String orderType) {
      //...
   }
}

with both these @RabbitListener(s) utilizing the same inherited pre/post message processing

I see there is a 'containerFactory' argument in the @RabbitListener annotation but i'm already declaring one in the config... and i'm really sure how to achieve the inheritance I desire with a custom containerFactory.


Updated Answer: This is what I ended up doing.

Advice defintion:

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.amqp.core.Message;

/**
 * AOP Around advice wrapper. Every time a message comes in we can do 
 * pre/post processing by using this advice by implementing the before/after methods.
 * @author sjacobs
 *
 */
public class RabbitListenerAroundAdvice implements MethodInterceptor {

    /**
     * place the "AroundAdvice" around each new message being processed.
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {

        Message message = (Message) invocation.getArguments()[1];

        before(message)
        Object result = invocation.proceed();
        after(message);

        return  result;
    }

declare beans: In your rabbitmq config declare the advice as a Spring bean and pass it to the rabbitListenerContainerFactory#setAdviceChain(...)

//...

    @Bean
    public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
        SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
        factory.setConnectionFactory( cachingConnectionFactory() );
        factory.setTaskExecutor(threadPoolTaskExecutor());
        factory.setMessageConverter(jackson2JsonMessageConverter());   

        factory.setAdviceChain(rabbitListenerAroundAdvice());

        return factory;
    }

    @Bean
    public RabbitListenerAroundAdvice rabbitListenerAroundAdvice() {
        return new RabbitListenerAroundAdvice();
    }

// ...
Selwyn
  • 3,118
  • 1
  • 26
  • 33

2 Answers2

5

Correction

You can use the advice chain in the SimpleRabbitListenerContainerFactory to apply an around advice to listeners created for @RabbitListener; the two arguments are the Channel and Message.

If you only need to take action before calling the listener, you can add MessagePostProcessor(s) to the container afterReceivePostProcessors.

Gary Russell
  • 166,535
  • 14
  • 146
  • 179
  • I corrected my answer; I forgot that we expose the `adviceChain` property on the container factory. – Gary Russell Dec 29 '15 at 17:46
  • I understand that, we can configure the container by extending `AbstractRabbitListenerContainerFactoryConfigurer`, if so, isn't it better to make `Simple/DirectRabbitListenerContainerFactoryConfigurer` non-final so we can extend instead? – Muhammad Hewedy Nov 28 '18 at 11:13
  • Don't ask new questions in comments on an old answer; start a new question. Why do you need to extend the configurer? – Gary Russell Nov 28 '18 at 13:47
  • Correct, no need at all. we can simply redefine the container listener bean or use `brave tracing` way. https://gist.github.com/mhewedy/7de5d3aad0b7a06b1353b9ccbc1a54f5 – Muhammad Hewedy Nov 28 '18 at 19:22
0

The inheritance isn't possible here because annotation processing on the POJO methods and MessageListener implementation are fully different stories.

Using MessageListener you fully have control around the target behavior and the container.

With the annotations you deal only with the POJO, framework-free code. The particular MessageListener is created on the background. And that one fully based on the annotated method.

I'd say we can achieve your requirement using Spring AOP Framework.

See the recent question and its answers on the matter: How to write an integration test for @RabbitListener annotation?

Community
  • 1
  • 1
Artem Bilan
  • 113,505
  • 11
  • 91
  • 118