动态加载 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()
那里。
我需要能够将转换器添加到由 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()
那里。