动态加载 Spring 个集成组件

Dynamically loading Spring Integration components

我需要能够将转换器添加到由 MVC 应用程序管理的 运行 Spring 集成应用程序上下文中,为了实现这一点,我从一个简单的 POC 开始,我在其中加载了一个运行时的新转换器(启动时未扫描此转换器),此 POC 作为独立 java 应用程序运行,仅包含 spring 集成库,这是主要的 class.

@Component
public class DynamicApplication {

    @Autowired
    private ConfigurableApplicationContext appContext;

    @Autowired
    private BeanFactory beanFactory;

    private Map<String, String> channelsMap = new HashMap<String, String>();

    private Map<String, String> inboundGWMap = new HashMap<String, String>();

    public static void main(String a[]) throws Exception{
        DynamicApplication dynamicApplication = new DynamicApplication();
        GenericXmlApplicationContext context = dynamicApplication.setupContext();

        DynamicApplication localDynamicApplication = context.getBean(DynamicApplication.class);

        //then we wait for input to add new classes
        final Scanner scanner = new Scanner(System.in);

        while (true) {

            final String input = scanner.nextLine();

            if("q".equals(input.trim())) {
                break;
            }
            else{
                //in the case we got a package definition to be scanned
                localDynamicApplication.addClassesFromAnnotatedPackage(input);
                }
        }
    }

    public  GenericXmlApplicationContext setupContext() {
        final GenericXmlApplicationContext context = new GenericXmlApplicationContext();
        context.load("configuration/inbound-grand-central-configuration.xml");
        context.registerShutdownHook();
        context.refresh();
        DynamicApplication dynamicApplication = context.getBean(DynamicApplication.class);
        for(int i = 0 ; i < 1 ; i++){
            TcpInboundGateway listener = dynamicApplication.createTcpInboundGateway("server" + i, 9877 + i);
            listener.start();
        }

        return context;
    }


}

一个非常简单的服务激活器

package com.client.connector.inbound.config;

@MessageEndpoint
public class BussinesService {

    @ServiceActivator(inputChannel="toBSChannel")
    public String processIncomingMessage(String message) {
        System.out.println("Bussines logic: " + message);

        return "respondemos:" + message;

    }

}

还有一个初始转换器 包裹 com.client.connector.inbound.config;

@MessageEndpoint
public class ISOConverter {

    @Transformer(inputChannel="iSOConverterChannel", outputChannel="toBSChannel")
    public String iSOConverter(Object payload) throws Exception {
        System.out.println("ISO Conversion place");
        if (payload instanceof byte[]) {
            return new String((byte[]) payload);
        } 
        else if (payload instanceof char[]) {
            return new String((char[]) payload);
        } 
        else { 
            return payload.toString();
        } 
    }

    @Transformer(inputChannel="iSOConverterChannel", outputChannel="toBSChannel")
    public String iSOConverter(byte[] payload) throws Exception {
        System.out.println("ISO Conversion place");
        if (payload instanceof byte[]) {
            return new String((byte[]) payload);
        } 
        else { 
            return payload.toString();
        } 
    }

}

和 xml 配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    ..............................
    ............................">
    <description>Inbound Connectors common configuration file.
    </description>
    <int:annotation-config />
    <context:component-scan base-package="com.client.connector.inbound" />
</beans>

然后我有另一个在启动时未扫描的转换器。

package com.client.inboundtest.newclass;

@MessageEndpoint
public class DinamicAddedConverter {

    @Transformer(inputChannel="iSOConverterChannel", outputChannel="toBSChannel")
    public String iSOConverter2(Object payload) throws Exception {
        System.out.println("ISO conversion from new class");
        if (payload instanceof byte[]) {
            return new String((byte[]) payload);
        } 
        else if (payload instanceof char[]) {
            return new String((char[]) payload);
        } 
        else { 
            return payload.toString();
        } 
    }

    @Transformer(inputChannel="iSOConverterChannel", outputChannel="toBSChannel")
    public String iSOConverter2(byte[] payload) throws Exception {
        System.out.println("ISO conversion from new class");
        if (payload instanceof byte[]) {
            return new String((byte[]) payload);
        } 
        else { 
            return payload.toString();
        } 
    }

}

当我启动程序并使用 telnet 连接到端口 9877 并设置单词测试时,我在 java 应用程序

的输出中得到了这个
ISO Conversion place
Bussines logic: test
ISO Conversion place
Bussines logic: test

如果我在 java 控制台中编写包 com.client.inboundtest.newclass,它会加载新的转换器,并显示日志

com.client.inboundtest.newclass
Added class com.client.inboundtest.newclass.DinamicAddedConverter
Nov 11, 2015 7:41:51 PM org.springframework.integration.endpoint.EventDrivenConsumer logComponentSubscriptionEvent
INFO: Adding {transformer:com.client.inboundtest.newclass.DinamicAddedConverter.iSOConverter2.transformer} as a subscriber to the 'iSOConverterChannel' channel
Nov 11, 2015 7:41:51 PM org.springframework.integration.channel.AbstractSubscribableChannel adjustCounterIfNecessary
INFO: Channel 'org.springframework.context.support.GenericXmlApplicationContext@4d9cad9d.iSOConverterChannel' has 3 subscriber(s).
Nov 11, 2015 7:41:51 PM org.springframework.integration.endpoint.AbstractEndpoint start
INFO: started com.client.inboundtest.newclass.DinamicAddedConverter.iSOConverter2.transformer
Nov 11, 2015 7:41:51 PM org.springframework.integration.endpoint.EventDrivenConsumer logComponentSubscriptionEvent
INFO: Adding {transformer} as a subscriber to the 'iSOConverterChannel' channel
Nov 11, 2015 7:41:51 PM org.springframework.integration.channel.AbstractSubscribableChannel adjustCounterIfNecessary
INFO: Channel 'org.springframework.context.support.GenericXmlApplicationContext@4d9cad9d.iSOConverterChannel' has 4 subscriber(s).
Nov 11, 2015 7:41:51 PM org.springframework.integration.endpoint.AbstractEndpoint start
INFO: started com.client.inboundtest.newclass.DinamicAddedConverter.iSOConverter2.transformer#2

并向同一个打开的 telnet 连接发送一条新消息会在 java 应用程序控制台上提供此输出

ISO Conversion place
Bussines logic: test2
ISO Conversion place
Bussines logic: test2
ISO conversion from new class
Bussines logic: test2
ISO conversion from new class
Bussines logic: test2

这是我所期望的。

然后我尝试将我的 POC 移动到 Spring MVC 上下文我开始看到意外行为,第一个意外的事情是通道不是动态创建的,就像在第一个 POC 中发生的那样,所以我需要像这样在上下文文件中明确定义它们

<beans ........">
    <context:component-scan base-package="main.package" />

    <int:annotation-config />

    <int:channel id="toBSChannel" />
    <int:channel id="iSOConverterChannel" />
    <int:channel id="restRequestChannel" />

    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix">
            <value>/WEB-INF/views/</value>
        </property>
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>
</beans>

我所做的是创建一个连接到我原来的 DynamicApplication 的新控制器,并通过 @PostConstruct 在侦听器启动的 bean 上将静态调用更改为设置。

当应用程序启动时,它会显示我的 serlvlet 以及端口 9877 上的侦听器,当我调用刷新 servlet 时,它显示的输出与我从 java 应用程序获得的输出完全相同:

Added class com.client.inboundtest.newclass.ConverterA
Nov 11, 2015 7:55:59 PM org.springframework.integration.endpoint.EventDrivenConsumer logComponentSubscriptionEvent
INFO: Adding {transformer:com.client.inboundtest.newclass.ConverterA.iSOConverter.transformer} as a subscriber to the 'iSOConverterChannel' channel
Nov 11, 2015 7:55:59 PM org.springframework.integration.channel.AbstractSubscribableChannel adjustCounterIfNecessary
INFO: Channel 'org.springframework.web.context.WebApplicationContext:/InboundDinamicGC/dispatcher.iSOConverterChannel' has 7 subscriber(s).
Nov 11, 2015 7:55:59 PM org.springframework.integration.endpoint.AbstractEndpoint start
INFO: started com.client.inboundtest.newclass.ConverterA.iSOConverter.transformer
Nov 11, 2015 7:55:59 PM org.springframework.integration.endpoint.EventDrivenConsumer logComponentSubscriptionEvent
INFO: Adding {transformer} as a subscriber to the 'iSOConverterChannel' channel
Nov 11, 2015 7:55:59 PM org.springframework.integration.channel.AbstractSubscribableChannel adjustCounterIfNecessary
INFO: Channel 'org.springframework.web.context.WebApplicationContext:/InboundDinamicGC/dispatcher.iSOConverterChannel' has 8 subscriber(s).
Nov 11, 2015 7:55:59 PM org.springframework.integration.endpoint.AbstractEndpoint start
INFO: started com.client.inboundtest.newclass.ConverterA.iSOConverter.transformer#2

问题是消息永远不会像在普通 java POC 上那样到达新的转换器,有什么想法吗?

请查看类似问题 How to hook up a list of message driven adapters without actually writing each one out? 及其讨论,以及那里的链接。

您应该做的只是创建一个 child 应用程序上下文。就像您在 setupContext() 中所做的那样,当然,将 MVC parent 推到 refresh() 那里。