@StreamListener注解解释

原作者文章指路: https://blog.csdn.net/qq_28802119/article/details/106907523

一直被@StreamListener注解带来的恐惧所支配。今天来揭开它的面纱。

MAVEN引入相关jar包(版本2.0.1)
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-stream-kafka</artifactId>
        </dependency>

@StreamListener注解使用
    @StreamListener
    public void test(){
    
    }

相关源码分析
先点开@StreamListener注解源码:

@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@MessageMapping
@Documented
public @interface StreamListener {

    /**
     * The name of the binding target (e.g. channel) that the method subscribes to.
     * @return the name of the binding target.
     */
    @AliasFor("target")
    String value() default "";

    /**
     * The name of the binding target (e.g. channel) that the method subscribes to.
     * @return the name of the binding target.
     */
    @AliasFor("value")
    String target() default "";

    /**
     * A condition that must be met by all items that are dispatched to this method.
     * @return a SpEL expression that must evaluate to a {@code boolean} value.
     */
    String condition() default "";

    /**
     * When "true" (default), and a {@code @SendTo} annotation is present, copy the
     * inbound headers to the outbound message (if the header is absent on the outbound
     * message). Can be an expression ({@code #{...}}) or property placeholder. Must
     * resolve to a boolean or a string that is parsed by {@code Boolean.parseBoolean()}.
     * An expression that resolves to {@code null} is interpreted to mean {@code false}.
     *
     * The expression is evaluated during application initialization, and not for each
     * individual message.
     *
     * Prior to version 1.3.0, the default value used to be "false" and headers were
     * not propagated by default.
     *
     * Starting with version 1.3.0, the default value is "true".
     *
     * @since 1.2.3
     */
    String copyHeaders() default "true";

}

这是它所在的包结构:

选中@StreamListener注解value()方法看谁调用了它:

可以看到下面的方法又调用了上面的方法,所以只需要展开下面的方法即可:

直接点击run()方法,就到了处理@StreamListener注解的代码入口(部分代码):

public class StreamListenerAnnotationBeanPostProcessor implements BeanPostProcessor, ApplicationContextAware, SmartInitializingSingleton {
    @Override
    public final Object postProcessAfterInitialization(Object bean, final String beanName) throws BeansException {
        Class<?> targetClass = AopUtils.isAopProxy(bean) ? AopUtils.getTargetClass(bean) : bean.getClass(); //找到bean对象的真实类型
        Method[] uniqueDeclaredMethods = ReflectionUtils.getUniqueDeclaredMethods(targetClass); //找到bean对象的方法
        //逐个遍历并处理声明了@StreamListener注解的方法
        for (Method method : uniqueDeclaredMethods) {
            //找声明了@StreamListener注解的方法
            StreamListener streamListener = AnnotatedElementUtils.findMergedAnnotation(method, StreamListener.class);
            if (streamListener != null && !method.isBridge()) {
                streamListenerCallbacks.add(() -> {
                    Assert.isTrue(method.getAnnotation(Input.class) == null, StreamListenerErrorMessages.INPUT_AT_STREAM_LISTENER); //被@StreamListener注解声明的方法上不允许再声明@Input注解
                    this.doPostProcess(streamListener, method, bean);
                });
            }
        }
        return bean;
    }
}

我们看看doPostProcess()方法中做了啥

    private void doPostProcess(StreamListener streamListener, Method method, Object bean) {
        streamListener = postProcessAnnotation(streamListener, method);
        Optional<StreamListenerSetupMethodOrchestrator> streamListenerSetupMethodOrchestratorAvailable =
                streamListenerSetupMethodOrchestrators.stream()
                        .filter(t -> t.supports(method))
                        .findFirst();
        Assert.isTrue(streamListenerSetupMethodOrchestratorAvailable.isPresent(),
                "A matching StreamListenerSetupMethodOrchestrator must be present");
        StreamListenerSetupMethodOrchestrator streamListenerSetupMethodOrchestrator = streamListenerSetupMethodOrchestratorAvailable.get();
        streamListenerSetupMethodOrchestrator.orchestrateStreamListenerSetupMethod(streamListener, method, bean);
    }
    
    //啥也没干的一个方法,其实是一个扩展点
    protected StreamListener postProcessAnnotation(StreamListener originalAnnotation, Method annotatedMethod) {
        return originalAnnotation;
    }

StreamListenerSetupMethodOrchestrator类是个接口,它的实现在StreamListenerAnnotationBeanPostProcessor类的内部类DefaultStreamListenerSetupMethodOrchestrator中:

private class DefaultStreamListenerSetupMethodOrchestrator implements StreamListenerSetupMethodOrchestrator
        @Override
        public void orchestrateStreamListenerSetupMethod(StreamListener streamListener, Method method, Object bean) {
            String methodAnnotatedInboundName = streamListener.value();  //处理@StreamListener注解的value值

            String methodAnnotatedOutboundName = StreamListenerMethodUtils.getOutboundBindingTargetName(method); //解析输出通道
            int inputAnnotationCount = StreamListenerMethodUtils.inputAnnotationCount(method);
            int outputAnnotationCount = StreamListenerMethodUtils.outputAnnotationCount(method);
            boolean isDeclarative = checkDeclarativeMethod(method, methodAnnotatedInboundName, methodAnnotatedOutboundName);
            StreamListenerMethodUtils.validateStreamListenerMethod(method,
                    inputAnnotationCount, outputAnnotationCount,
                    methodAnnotatedInboundName, methodAnnotatedOutboundName,
                    isDeclarative, streamListener.condition());
            if (isDeclarative) {
                StreamListenerParameterAdapter[] toSlpaArray = new StreamListenerParameterAdapter[this.streamListenerParameterAdapters.size()];
                Object[] adaptedInboundArguments = adaptAndRetrieveInboundArguments(method, methodAnnotatedInboundName,
                        this.applicationContext,
                        this.streamListenerParameterAdapters.toArray(toSlpaArray));
                invokeStreamListenerResultAdapter(method, bean, methodAnnotatedOutboundName, adaptedInboundArguments);
            } else {
                registerHandlerMethodOnListenedChannel(method, streamListener, bean);
            }
        }
}

可以看到,这段代码中完成了对@StreamListener注解的解析,并在最后调用了registerHandlerMethodOnListenedChannel()方法完成了注册,看一下具体实现:

        private void registerHandlerMethodOnListenedChannel(Method method, StreamListener streamListener, Object bean) {
            Assert.hasText(streamListener.value(), "The binding name cannot be null");
            if (!StringUtils.hasText(streamListener.value())) {
                throw new BeanInitializationException("A bound component name must be specified");
            }
            final String defaultOutputChannel = StreamListenerMethodUtils.getOutboundBindingTargetName(method);
            if (Void.TYPE.equals(method.getReturnType())) {
                Assert.isTrue(StringUtils.isEmpty(defaultOutputChannel),
                        "An output channel cannot be specified for a method that does not return a value");
            }
            else {
                Assert.isTrue(!StringUtils.isEmpty(defaultOutputChannel),
                        "An output channel must be specified for a method that can return a value");
            }
            StreamListenerMethodUtils.validateStreamListenerMessageHandler(method);
            mappedListenerMethods.add(streamListener.value(),
                    new StreamListenerHandlerMethodMapping(bean, method, streamListener.condition(), defaultOutputChannel,
                            streamListener.copyHeaders()));
        }
所以呢,最后的结果是,@StreamListener注解标注的方法会在系统启动时由StreamListenerAnnotationBeanPostProcessor类发起解析,然后被StreamListenerAnnotationBeanPostProcessor类的内部类DefaultStreamListenerSetupMethodOrchestrator解析并注册到mappedListenerMethods中,看一下mappedListenerMethods是个啥:

private final MultiValueMap<String, StreamListenerHandlerMethodMapping> mappedListenerMethods = new LinkedMultiValueMap<>();
1
额…一个map而已。name问题来了,注册完了,它是怎么工作的?在StreamListenerAnnotationBeanPostProcessor类全局搜mappedListenerMethods,可以看到它出现在了这段代码里:

    @Override
    public final void afterSingletonsInstantiated() {
        this.injectAndPostProcessDependencies();
        EvaluationContext evaluationContext = IntegrationContextUtils.getEvaluationContext(this.applicationContext.getBeanFactory());
        for (Map.Entry<String, List<StreamListenerHandlerMethodMapping>> mappedBindingEntry : mappedListenerMethods
                .entrySet()) {
            ArrayList<DispatchingStreamListenerMessageHandler.ConditionalStreamListenerMessageHandlerWrapper> handlers = new ArrayList<>();
            for (StreamListenerHandlerMethodMapping mapping : mappedBindingEntry.getValue()) {
                final InvocableHandlerMethod invocableHandlerMethod = this.messageHandlerMethodFactory
                        .createInvocableHandlerMethod(mapping.getTargetBean(),
                                checkProxy(mapping.getMethod(), mapping.getTargetBean()));
                StreamListenerMessageHandler streamListenerMessageHandler = new StreamListenerMessageHandler(
                        invocableHandlerMethod, resolveExpressionAsBoolean(mapping.getCopyHeaders(), "copyHeaders"),
                        springIntegrationProperties.getMessageHandlerNotPropagatedHeaders());
                streamListenerMessageHandler.setApplicationContext(this.applicationContext);
                streamListenerMessageHandler.setBeanFactory(this.applicationContext.getBeanFactory());
                if (StringUtils.hasText(mapping.getDefaultOutputChannel())) {
                    streamListenerMessageHandler.setOutputChannelName(mapping.getDefaultOutputChannel());
                }
                streamListenerMessageHandler.afterPropertiesSet();
                if (StringUtils.hasText(mapping.getCondition())) {
                    String conditionAsString = resolveExpressionAsString(mapping.getCondition(), "condition");
                    Expression condition = SPEL_EXPRESSION_PARSER.parseExpression(conditionAsString);
                    handlers.add(
                            new DispatchingStreamListenerMessageHandler.ConditionalStreamListenerMessageHandlerWrapper(
                                    condition, streamListenerMessageHandler));
                }
                else {
                    handlers.add(
                            new DispatchingStreamListenerMessageHandler.ConditionalStreamListenerMessageHandlerWrapper(
                                    null, streamListenerMessageHandler));
                }
            }
            if (handlers.size() > 1) {
                for (DispatchingStreamListenerMessageHandler.ConditionalStreamListenerMessageHandlerWrapper handler : handlers) {
                    Assert.isTrue(handler.isVoid(), StreamListenerErrorMessages.MULTIPLE_VALUE_RETURNING_METHODS);
                }
            }
            AbstractReplyProducingMessageHandler handler;

            if (handlers.size() > 1 || handlers.get(0).getCondition() != null) {
                handler = new DispatchingStreamListenerMessageHandler(handlers, evaluationContext);
            }
            else {
                handler = handlers.get(0).getStreamListenerMessageHandler();
            }
            handler.setApplicationContext(this.applicationContext);
            handler.setChannelResolver(this.binderAwareChannelResolver);
            handler.afterPropertiesSet();
            this.applicationContext.getBeanFactory().registerSingleton(handler.getClass().getSimpleName() + handler.hashCode(), handler);
            applicationContext.getBean(mappedBindingEntry.getKey(), SubscribableChannel.class).subscribe(handler);
        }
        this.mappedListenerMethods.clear();
    }

真是好头疼的一大段代码啊,完全不想看干嘛的。所以,偷个懒?简化下它:

    @Override
    public final void afterSingletonsInstantiated() {
        for (Map.Entry<String, List<StreamListenerHandlerMethodMapping>> mappedBindingEntry : mappedListenerMethods
                .entrySet()) {
            applicationContext.getBean(mappedBindingEntry.getKey(), SubscribableChannel.class).subscribe(handler);
        }
        this.mappedListenerMethods.clear();
    }

是不是舒服多了?做了什么一目了然啊!遍历整个mappedListenerMethods,按个取出元素,一同猛如虎的操作,然后取出了bean名称为mappedBindingEntry.getKey(),类型为SubscribableChannel的bean对象执行了下subscribe()方法。然后,就订阅了MQ消息???

嗯,终于看完了。就这样吧。
————————————————
版权声明:本文为CSDN博主「小雨的光」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_28802119/article/details/106907523

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值