[英]Dynamically loading Spring Integration components
I need to be able to add converters to a running Spring integration application context managed by an MVC application, in order to achieve that, I started by a simple POC where I load a new converter on runtime (this converter wasn't scanned at startup), this POC runs as a standalone java app and only includes spring integration libraries, here is the main class.我需要能够将转换器添加到由 MVC 应用程序管理的正在运行的 Spring 集成应用程序上下文中,为了实现这一点,我从一个简单的 POC 开始,我在运行时加载一个新的转换器(此转换器在启动时未扫描),这个 POC 作为一个独立的 java 应用程序运行,只包含 spring 集成库,这里是主类。
@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;
}
}
a very simple service activator一个非常简单的服务激活器
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;
}
}
and also a initial converter package com.client.connector.inbound.config;以及一个初始转换器包 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();
}
}
}
and the xml config file和 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>
And then I have another converter which wasn't scanned at startup.然后我有另一个在启动时未扫描的转换器。
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();
}
}
}
When I start the program, and connect to port 9877 using telnet, and set the word test, I got this in the output of the java app当我启动程序并使用 telnet 连接到端口 9877 并设置单词 test 时,我在 java 应用程序的输出中得到了这个
ISO Conversion place
Bussines logic: test
ISO Conversion place
Bussines logic: test
The, if I write the package com.client.inboundtest.newclass in the java console, it load the new converter, and shows the log如果我在 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
and sending a new message to the same opened telnet connection gives this output on the java app console并向同一个打开的 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
Which is what I expect.这是我所期望的。
Then I tried to move my POC to a Spring MVC context I started seeing unexpected behaviors, the first non-expected thing was that the channels wasn't dynamically created as it was happening in the first POC, so I need to define them explicit on the context file like this然后我尝试将我的 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>
What I did was to create a new controller wired to my original DynamicApplication and change the static call to setup by a @PostConstruct on the bean son the listeners are started.我所做的是创建一个连接到我原来的 DynamicApplication 的新控制器,并通过 @PostConstruct 在监听器启动的 bean 上更改对 setup 的静态调用。
When the app starts, it shows my serlvlets along with my listener on port 9877, and when I call the refresh servlet, it is showing the exact same output I got from the java app:当应用程序启动时,它会在端口 9877 上显示我的 serlvlets 和我的侦听器,当我调用刷新 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
The problem is that the message never reaches the new converter as it happened on the plain java POC, any ideas?问题是消息永远不会到达新的转换器,因为它发生在普通的 Java POC 上,有什么想法吗?
Please, take a look to similar question How to hook up a list of message driven adapters without actually writing each one out?请看看类似的问题如何在不实际写出每个适配器的情况下连接消息驱动适配器列表? and its discussion, plus links there.
及其讨论,以及那里的链接。
What you should do is just create a child
application context.您应该做的只是创建一个
child
应用程序上下文。 Like you do you in your setupContext()
, but push that MVC parent
there and refresh()
, of course.就像你在
setupContext()
所做的那样,但当然,将那个 MVC parent
推送到那里和refresh()
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.