Spring Boot 2.0.2,用Aop拦截Cloud Stream注释不再工作

时间:2018-06-04 14:18:46

标签: java spring spring-boot spring-aop spring-cloud-stream

我尽量保持标题的清晰和简单。

基本上,我需要拦截Cloud stream的@Input和@Output注释的使用。这需要自动将特定的ChannelInterceptor添加到每个MessageChannel。 (无论消息是否已生成或消耗,preSend方法中的行为都会略有不同。)

例如,如果我声明此建议

@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;
}

我宣布这个示例类

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

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

这对Spring Boot 2.0.1完全没问题,但Spring Boot 2.0.2却没有,我很难理解为什么。

我没有尝试过其他Cloud stream的注释,但基本的Aop工作正常。

请记住,这是为了在JAR中使用,因此我事先并不知道将要使用的类或通道名称,我需要这对开发人员来说是自动且透明的。 / p>

谢谢!

编辑:如果读取此内容的人不熟悉云流,则Sink接口会声明一个使用@Input注释的方法,因此启用绑定就可以解决问题。

2 个答案:

答案 0 :(得分:1)

因此,BPP并没有完全解决这个问题,因为我需要将使用@Input创建的MessageChannel与使用@Output创建的MessageChannel区分开来。 MessageChannel bean不携带此信息。这就是为什么我首先使用Aop来分别拦截这两个注释。

洞察力:我还想过将@GlobalChannelInterceptor与包含“input”或“output”的模式一起使用,但这意味着要对最终用户强制执行这些模式。我将此解决方案作为最后的手段,但我希望这个过程在使用jar时完全不可见且无冲击力。这就是AOP派上用场的地方,但2.0.2中的这种新行为在我的情况下肯定是有问题的。

编辑:所以版本更改的问题是bean初始化顺序,对于任何与Spring boot 2.0.2有类似问题的人。如果您可以控制所需的每个bean,我建议您查看@DependsOn。

最终,我通过使用BeanPostProcessor而不是AOP来解决我的具体问题,将输入与输出分开,正如@Oleg Zhurakousky所建议的那样。 以下是一种工作方法:

@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;
    }

答案 1 :(得分:0)

不确定boot 2.0.1和2.0.2之间发生了什么,但上面的声音听起来像是一种非常复杂的简单方法。为什么不在初始化时添加前/后通道拦截器的位置注册BPP。