【问题标题】:Spring integration 'Dispatcher has no subscribers' when channels are used in init-method当在 init 方法中使用通道时,Spring 集成“调度程序没有订阅者”
【发布时间】:2012-06-21 10:14:39
【问题描述】:

在 Spring bean 的 init 方法中尝试将消息发布到通道时出现“调度程序没有订阅者”错误。请看下面的例子:

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
    xmlns:rmi="http://www.springframework.org/schema/integration/rmi"
    xmlns:int="http://www.springframework.org/schema/integration"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/integration
        http://www.springframework.org/schema/integration/spring-integration.xsd
        http://www.springframework.org/schema/integration/rmi
        http://www.springframework.org/schema/integration/rmi/spring-integration-rmi.xsd">

    <bean id="currencyService" class="com.demo.CurrencyService" init-method="init"/>

    <int:channel id="currencyChannel" />
    <int:channel id="currencyReplyChannel">
        <int:queue/>
    </int:channel>
    <rmi:outbound-gateway id="currencyServiceGateway"
        request-channel="currencyChannel" remote-channel="currencyServiceChannel"
        reply-channel="currencyReplyChannel" host="localhost" port="2197" />
</beans>

Spring 托管 bean:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.integration.Message;
import org.springframework.integration.MessageChannel;
import org.springframework.integration.core.MessagingTemplate;
import org.springframework.integration.core.PollableChannel;
import org.springframework.integration.message.GenericMessage;

public class CurrencyService {

    @Autowired
    private MessageChannel currencyChannel;

    @Autowired
    private PollableChannel currencyReplyChannel;

    private CurrencyListBO currencyListBO;

    public CurrencyListBO getCurrencyList() {
        return currencyListBO;
    }

    public void init() {
        CurrencyIN request = new CurrencyIN();
        request.setChannelCode("RMW");
        request.setTransactionType(CurrencyIN.TransactionType.currencyLoaderService
                .toString());
        GenericMessage<IRequestBO> message = new GenericMessage<IRequestBO>(
                request);
        MessagingTemplate template = new MessagingTemplate();
        template.send(currencyChannel, message);
        Message<CurrencyListBO> reply = template.receive(currencyReplyChannel);
        currencyListBO = reply.getPayload();
    }
}

如果在第一次调用之后初始化 currencyListBO 而不是 init-method,则一切正常。

public CurrencyListBO getCurrencyList() {
    if(currencyListBO == null) {
        init();
    }
    return currencyListBO;
}

请告诉我第一种方法有什么问题。

【问题讨论】:

  • 即使使用 '@PostConstruct' 而不是 'init-method' 也会出现同样的问题。

标签: spring-integration


【解决方案1】:

init/@PostConstruct 方法在您的 bean 被实例化之后但在上下文的其余部分被连接之前(在这种情况下是在 RMI 适配器订阅通道之前)调用。

您需要等到上下文完全刷新。

一种方法是实现

ApplicationListener<ContextRefreshedEvent>

把你的代码放进去

public void onApplicationEvent(ContextRefreshedEvent event)

该方法将在上下文完全连接后调用。

【讨论】:

  • 非常感谢您的建议。这种方法奏效了。但是我必须编写一个技术服务来实现 ApplicationListener 并启动所有必需的业务服务。如果我尝试在业务服务本身上实现 ApplicationListener,我将无法将此业务服务作为我的单元测试的依赖项注入。似乎这种行为是正确和干净的(拥有一个 init 服务而不是在所有必需的业务接口上实现 ApplicationListener 接口)但想检查这是有意的还是我面临的错误。
  • 这是合理的做法;最好尽可能减少对框架的依赖。当然,您可以在单元测试中模拟事件。或者,当然,您可以使用在首次使用时而不是在 init 方法中运行 init 代码的技术。另一种技术是实现 SmartLifeCycle;将阶段设置为 Integer.MAX_VALUE 并且您的 bean 的 start() 方法将是最后被调用的方法之一(如果 isAutoStartup() 返回 true)。
  • 非常感谢您的帮助。
猜你喜欢
  • 2013-09-25
  • 2015-11-02
  • 2013-08-16
  • 1970-01-01
  • 1970-01-01
  • 2019-10-04
  • 2017-04-10
  • 1970-01-01
  • 2018-02-19
相关资源
最近更新 更多