java - Spring boot 2.0.2,使用 Aop 拦截 Cloud Stream 注释不再起作用
问题描述
我试图使标题尽可能明确和简单。
基本上,我需要拦截 Cloud 流的 @Input 和 @Output 注释的使用。这是为每个 MessageChannel 自动添加特定的 ChannelInterceptor 所必需的。(无论消息是被生成还是被消费,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 流的注释,但基本的 Aop 工作正常。
请记住,这意味着要在 JAR 中使用,因此我事先不知道将使用的类或通道名称,我需要它对开发人员来说是自动和透明的。
谢谢 !
编辑:如果任何阅读本文的人不熟悉 Cloud 流,Sink 接口声明了一个用 @Input 注释的方法,因此启用绑定就可以了。
解决方案
所以,BPP 并没有完全解决这个问题,因为我需要区分使用 @Input 创建的 MessageChannel 和使用 @Output 创建的 MessageChannel。MessageChannel bean 不携带此信息。这就是为什么我首先使用 Aop 来分别拦截这两个注释。
洞察力:我还考虑过将@GlobalChannelInterceptor 与包含“输入”或“输出”的模式一起使用,但这意味着对最终用户强制执行此类模式。我将这个解决方案作为最后的手段,但我希望这个过程在使用 jar 时完全不可见且无影响。这就是 AOP 派上用场的地方,但 2.0.2 中的这种新行为在我的情况下肯定是有问题的。
编辑:所以版本更改的问题是 bean 初始化顺序,对于任何与 Spring boot 2.0.2 有类似问题的人。如果您可以控制所需的每个 bean,我建议您查看 @DependsOn。
最终,正如@Oleg Zhurakousky 所建议的那样,我通过使用 BeanPostProcessor 而不是 AOP 将输入与输出分开来解决了我的特定问题。以下是一种工作方法:
@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;
}
推荐阅读
- python - 来自numpy数组的数据框,没有数据成为索引
- javafx - 阶段之间的 JavaFX 焦点侦听器
- php - PHP > XML 排序麻烦
- flutter - Flutter:Stack - Offstage - TextFormField - 焦点问题
- vba - 如何将光标放在打开的 FileDialog 的文件列表中?
- python - 将表转换为聚合表
- nativescript - nativescript sidedrawer 不起作用
- c++ - 使用 Visual Studio 在控制台中输出 utf8(宽流)
- xml - 从 Oracle 层次表创建 XML
- html - b-form-input 不接受正常 HTML 输入中的最小数字