Spring boot 2.0.2, interception of Cloud Stream annotations with Aop not working anymore

断了今生、忘了曾经 提交于 2021-02-19 10:00:33

问题


I tried to keep the title as explicit and simple as possible.

Basically, I need to intercept the usage of Cloud stream's @Input and @Output annotations. This is needed to automatically add a specific ChannelInterceptor to each MessageChannel. (The behaviour in the preSend method will be slightly different whether the message has been produced or consumed).

For example, if I declare this advice

@Around("@annotation(org.springframework.cloud.stream.annotation.Input)")
public Object interceptInput(ProceedingJoinPoint joinPoint) throws Throwable {
    LOG.debug("Intercepted @Input from method : {}", joinPoint.getSignature());

    Object returnValue = null;

    try {
        returnValue = joinPoint.proceed();
ChannelInterceptorManager.addInputInterceptor((AbstractMessageChannel)returnValue);
    } catch (Exception e) {
        LOG.warn("@Input error", e);
    }

    return returnValue;
}

and I declare this example class

@EnableBinding(Sink.class)
@Component
public class MyClass {

    @StreamListener(Sink.INPUT)
   public void handle(Object message) {
      // preSend has been called before this method
   }
}

This worked perfectly fine with Spring Boot 2.0.1, but not with Spring Boot 2.0.2 and I'm struggling to understand why.

I haven't tried other Cloud stream's annotations, but basic Aop works fine.

Keep in mind that this is meant to be used in a JAR, as such I don't know in advance the classes or the channel names which will be used, I need this to be automatic and transparent to the developer.

Thanks !

Edit : In case anyone reading this isn't familiar with Cloud stream, the Sink interface declares a method annotated with @Input, so enabling binding on it will do the trick.


回答1:


So, BPP doesn't fully solve the issue, as I need to differentiate MessageChannel created with @Input from those created with @Output. The MessageChannel bean does not carry this information. This is why I used Aop in the first place, to intercept those two annotations separately.

For insight : I have also thought of using @GlobalChannelInterceptor with patterns containing either "input" or "output", but that would mean enforcing such patterns to the end user. I'm keeping this solution as last resort, but I would like this process to be completely invisible and impact-less while using the jar. This is where AOP came in handy, but this new behaviour from 2.0.2 is certainly problematic in my case.

Edit : So the issue with the version change is the bean initialization order, for anyone having a similar issue with Spring boot 2.0.2. If you have control over each bean you need, I suggest you take a look at @DependsOn.

Ultimately, I solved my specific issue by using BeanPostProcessor instead of AOP to separate inputs from outputs, as suggested by @Oleg Zhurakousky. Below is a working method :

@Autowired
    private AbstractBeanFactory beanFactory;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof AbstractMessageChannel) {
            try {
                RootBeanDefinition beanDefinition = (RootBeanDefinition) beanFactory.getMergedBeanDefinition(beanName);
                Method method = beanDefinition.getResolvedFactoryMethod();

                if (method != null) {
                    if (AnnotationUtils.findAnnotation(method, Input.class) != null) {
                        ((AbstractMessageChannel)bean).addInterceptor(/*Your input ChannelInterceptor*/);
                    } else if (AnnotationUtils.findAnnotation(method, Output.class) != null) {
                        ((AbstractMessageChannel)bean).addInterceptor(/*Your output ChannelInterceptor*/);
                    }
                }
            } catch (Exception e) {
                // exception can be thrown by the bean factory
            }
        }

        return bean;
    }



回答2:


Not sure what happened between boot 2.0.1 and 2.0.2, but the above sounds like a pretty complex way of doing something simple. Why not just register BPP where you can add pre/post channel interceptors during initialization time.



来源:https://stackoverflow.com/questions/50682722/spring-boot-2-0-2-interception-of-cloud-stream-annotations-with-aop-not-working

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!