Spring 集成和原型范围之谜
Mystery around Spring Integration and prototype scope
嗯,很可能没有任何神秘之处,但我不够聪明,无法弄清楚我的问题是什么。不过平时毕竟是谜啊!
抱歉介绍,我的问题是原型范围似乎不适合我。我创建了一个带有 Spring 集成流的 REST 服务(在流的前面有一个 http 入站网关)。大多数 bean 的范围都是原型。我通过使用线程调用十次来测试流程。我还记录了 bean 引用(只需在被调用的对象中打印 'this'),我看到相同的引用十次!
e.g. org.protneut.server.common.utils.XmlToMapConverter@755d7bc2
据我所知,这意味着没有为 XmlToMapConverter 创建新实例,而是使用同一个实例十次。我说得对吗?
很可能我配置 Spring 不正确,但我就是找不到我错过的东西。
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/SpringIntegration-servlet.xml</param-value>
</context-param>
<servlet>
<servlet-name>SpringIntegration</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringIntegration</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Spring积分-servlet.xml
<beans ...>
<mvc:annotation-driven/>
<context:component-scan base-package="org.protneut.server.controller, org.protneut.server.common.persistence.service" />
<jpa:repositories base-package="org.protneut.server.common.persistence.repository"/>
<!-- ********************* importing the mysql config ********************* -->
<import resource="/mysql-config-context.xml"/>
<!-- ********************* importing the message flow ********************* -->
<import resource="classpath:META-INF/spring/integration/processing_req_wokflow.xml"/>
<tx:annotation-driven />
<!-- ************************************************************************* -->
<!-- ******************************** for JPA ******************************** -->
<!-- ************************************************************************* -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="org.protneut.server.common.persistence.model" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect"/>
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- ********************* the used property files ********************* -->
<context:property-placeholder location="classpath:protneut-server-config.properties"/>
<!--
****************************************************************************************
********************** Beans used in the Spring Integration flow **********************
****************************************************************************************
-->
<!-- it has to be prototype, it cannot be request as it is in an async call so it is not in the request! -->
<bean id="logManager" class="org.protneut.server.log.LogManager" scope="prototype"></bean>
<bean id="convertRestToWorkflowBean" class="org.protneut.server.rest.ConvertRestMessageToWorkflowBean" scope="prototype"/>
<bean id="xmlToMapConverter" class="org.protneut.server.common.utils.XmlToMapConverter" scope="prototype"/>
<bean id="serviceStorageManager" class="org.protneut.server.cache.ServiceStorageManager" scope="singleton">
<property name="cacheBeanDAO" ref="cacheBeanDAO"/>
</bean>
<bean id="serviceCall" class="org.protneut.server.call.ServiceCall" scope="prototype">
<property name="httpClient" ref="httpClient"/>
</bean>
<bean id="xmlResponseExtractor" class="org.protneut.server.extract.XmlResponseExtractor" scope="prototype">
<property name="xmlToMapConverter" ref="xmlToMapConverter"/>
</bean>
...
</beans>
流程配置
<?xml version="1.0" encoding="UTF-8"?>
<beans ..>
<task:executor id="async_executor" pool-size="50" />
<!-- ***************************************************************************************************** -->
<!-- ************************************* WORKFLOW STARTING ********************************************* -->
<!-- ***************************************************************************************************** -->
<int-http:inbound-gateway id="receivingRest-inboundGateway"
supported-methods="POST" path="/service/get"
request-payload-type="java.lang.String" reply-timeout="10000"
request-channel="arrivedRestReq_channel" auto-startup="true"
error-channel="error_channel" reply-channel="restResponse-channel" >
</int-http:inbound-gateway>
<int:channel id="arrivedRestReq_channel" scope="prototype"></int:channel>
<int:json-to-object-transformer type="java.util.Map"
input-channel="arrivedRestReq_channel"
output-channel="fromConvertToActivator-channel"
id="convertJsonToMap_">
</int:json-to-object-transformer>
<int:channel id="fromConvertToActivator-channel"></int:channel>
<int:service-activator
input-channel="fromConvertToActivator-channel"
output-channel="toCallChain-channel"
id="convertRestToWorkflowBean-serviceActivator"
ref="convertRestToWorkflowBean" method="convert">
</int:service-activator>
<int:channel id="toCallChain-channel"></int:channel>
<int:chain input-channel="toCallChain-channel" id="call_chain">
<int:service-activator
id="serviceStorageManager-serviceActivator"
ref="serviceStorageManager" method="getServiceInfo">
</int:service-activator>
<int:service-activator id="serviceRequestCreator-serviceActivator" ref="serviceRequestCreator" method="create"/>
<int:service-activator id="call-serviceActivator"
ref="serviceCall" method="call">
</int:service-activator>
<int:router expression="payload.extractType.name()"
id="responseExtractor-router">
<int:mapping value="XPATH" channel="xmlResponse-channel"/>
<int:mapping value="JSONPATH" channel="jsonResponse-channel"/>
</int:router>
</int:chain>
...
<int:service-activator id="xmlResponseExtractor-serviceActivator"
ref="xmlResponseExtractor" method="extract" input-channel="xmlResponse-channel" output-channel="toRestResponseCreator_chain"></int:service-activator>
</beans>
所以我将 XmlToMapConverter 的范围定义为原型,但我仍然无法在新请求中拥有新对象。 convertRestToWorkflowBean 的情况相同,它是流中的第一个服务调用(服务激活器)。
你能解释一下问题出在哪里吗?
谢谢,V.
每次调用 ApplicationContext.getBean(...)
时都会创建原型作用域 bean
您已经包含了 bean 定义,但没有显示其他服务如何引用它。我的猜测是它在初始化期间被注入到单例服务中一次,因此只有一个。也许你需要每次调用 ApplicationContext.getBean() 来获取一个新实例。
还有其他涉及最终调用 getBean() 的动态代理的解决方案,我现在在手机上很难找到适合您的 link。
我没有看到 xmlToMapConverter
用法,但我看到了这个:
<int:service-activator
input-channel="fromConvertToActivator-channel"
output-channel="toCallChain-channel"
id="convertRestToWorkflowBean-serviceActivator"
ref="convertRestToWorkflowBean" method="convert">
你在哪里使用它:
<bean id="convertRestToWorkflowBean" class="org.protneut.server.rest.ConvertRestMessageToWorkflowBean" scope="prototype"/>
您遇到的问题是scope impendance
。那是因为 <int:service-activator>
填充了几个 singleton
beans,因此对你的 prototype
的引用也变成了 singleton
。
克服这个问题的一种方法是从那里使用 SpEL:
<int:service-activator
input-channel="fromConvertToActivator-channel"
output-channel="toCallChain-channel"
id="convertRestToWorkflowBean-serviceActivator"
expression="@convertRestToWorkflowBean.convert(payload)"/>
在这种情况下,每次调用时都会从 BeanFactory
中检索您的 convertRestToWorkflowBean
。
继续前进的另一个技巧如下:
<bean id="convertRestToWorkflowBean" class="org.protneut.server.rest.ConvertRestMessageToWorkflowBean" scope="prototype">
<aop:scoped-proxy/>
</bean>
在这种情况下,您的 bean 将被包装到 ScopedProxyFactoryBean
并且所有调用都将根据需要委托给您的 prototype
。
嗯,很可能没有任何神秘之处,但我不够聪明,无法弄清楚我的问题是什么。不过平时毕竟是谜啊!
抱歉介绍,我的问题是原型范围似乎不适合我。我创建了一个带有 Spring 集成流的 REST 服务(在流的前面有一个 http 入站网关)。大多数 bean 的范围都是原型。我通过使用线程调用十次来测试流程。我还记录了 bean 引用(只需在被调用的对象中打印 'this'),我看到相同的引用十次!
e.g. org.protneut.server.common.utils.XmlToMapConverter@755d7bc2
据我所知,这意味着没有为 XmlToMapConverter 创建新实例,而是使用同一个实例十次。我说得对吗?
很可能我配置 Spring 不正确,但我就是找不到我错过的东西。
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.4"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/SpringIntegration-servlet.xml</param-value>
</context-param>
<servlet>
<servlet-name>SpringIntegration</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value></param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringIntegration</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
Spring积分-servlet.xml
<beans ...>
<mvc:annotation-driven/>
<context:component-scan base-package="org.protneut.server.controller, org.protneut.server.common.persistence.service" />
<jpa:repositories base-package="org.protneut.server.common.persistence.repository"/>
<!-- ********************* importing the mysql config ********************* -->
<import resource="/mysql-config-context.xml"/>
<!-- ********************* importing the message flow ********************* -->
<import resource="classpath:META-INF/spring/integration/processing_req_wokflow.xml"/>
<tx:annotation-driven />
<!-- ************************************************************************* -->
<!-- ******************************** for JPA ******************************** -->
<!-- ************************************************************************* -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="packagesToScan" value="org.protneut.server.common.persistence.model" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="databasePlatform" value="org.hibernate.dialect.MySQL5Dialect"/>
</bean>
</property>
<property name="jpaProperties">
<props>
<prop key="hibernate.show_sql">false</prop>
<prop key="hibernate.format_sql">true</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<!-- ********************* the used property files ********************* -->
<context:property-placeholder location="classpath:protneut-server-config.properties"/>
<!--
****************************************************************************************
********************** Beans used in the Spring Integration flow **********************
****************************************************************************************
-->
<!-- it has to be prototype, it cannot be request as it is in an async call so it is not in the request! -->
<bean id="logManager" class="org.protneut.server.log.LogManager" scope="prototype"></bean>
<bean id="convertRestToWorkflowBean" class="org.protneut.server.rest.ConvertRestMessageToWorkflowBean" scope="prototype"/>
<bean id="xmlToMapConverter" class="org.protneut.server.common.utils.XmlToMapConverter" scope="prototype"/>
<bean id="serviceStorageManager" class="org.protneut.server.cache.ServiceStorageManager" scope="singleton">
<property name="cacheBeanDAO" ref="cacheBeanDAO"/>
</bean>
<bean id="serviceCall" class="org.protneut.server.call.ServiceCall" scope="prototype">
<property name="httpClient" ref="httpClient"/>
</bean>
<bean id="xmlResponseExtractor" class="org.protneut.server.extract.XmlResponseExtractor" scope="prototype">
<property name="xmlToMapConverter" ref="xmlToMapConverter"/>
</bean>
...
</beans>
流程配置
<?xml version="1.0" encoding="UTF-8"?>
<beans ..>
<task:executor id="async_executor" pool-size="50" />
<!-- ***************************************************************************************************** -->
<!-- ************************************* WORKFLOW STARTING ********************************************* -->
<!-- ***************************************************************************************************** -->
<int-http:inbound-gateway id="receivingRest-inboundGateway"
supported-methods="POST" path="/service/get"
request-payload-type="java.lang.String" reply-timeout="10000"
request-channel="arrivedRestReq_channel" auto-startup="true"
error-channel="error_channel" reply-channel="restResponse-channel" >
</int-http:inbound-gateway>
<int:channel id="arrivedRestReq_channel" scope="prototype"></int:channel>
<int:json-to-object-transformer type="java.util.Map"
input-channel="arrivedRestReq_channel"
output-channel="fromConvertToActivator-channel"
id="convertJsonToMap_">
</int:json-to-object-transformer>
<int:channel id="fromConvertToActivator-channel"></int:channel>
<int:service-activator
input-channel="fromConvertToActivator-channel"
output-channel="toCallChain-channel"
id="convertRestToWorkflowBean-serviceActivator"
ref="convertRestToWorkflowBean" method="convert">
</int:service-activator>
<int:channel id="toCallChain-channel"></int:channel>
<int:chain input-channel="toCallChain-channel" id="call_chain">
<int:service-activator
id="serviceStorageManager-serviceActivator"
ref="serviceStorageManager" method="getServiceInfo">
</int:service-activator>
<int:service-activator id="serviceRequestCreator-serviceActivator" ref="serviceRequestCreator" method="create"/>
<int:service-activator id="call-serviceActivator"
ref="serviceCall" method="call">
</int:service-activator>
<int:router expression="payload.extractType.name()"
id="responseExtractor-router">
<int:mapping value="XPATH" channel="xmlResponse-channel"/>
<int:mapping value="JSONPATH" channel="jsonResponse-channel"/>
</int:router>
</int:chain>
...
<int:service-activator id="xmlResponseExtractor-serviceActivator"
ref="xmlResponseExtractor" method="extract" input-channel="xmlResponse-channel" output-channel="toRestResponseCreator_chain"></int:service-activator>
</beans>
所以我将 XmlToMapConverter 的范围定义为原型,但我仍然无法在新请求中拥有新对象。 convertRestToWorkflowBean 的情况相同,它是流中的第一个服务调用(服务激活器)。
你能解释一下问题出在哪里吗?
谢谢,V.
每次调用 ApplicationContext.getBean(...)
时都会创建原型作用域 bean您已经包含了 bean 定义,但没有显示其他服务如何引用它。我的猜测是它在初始化期间被注入到单例服务中一次,因此只有一个。也许你需要每次调用 ApplicationContext.getBean() 来获取一个新实例。
还有其他涉及最终调用 getBean() 的动态代理的解决方案,我现在在手机上很难找到适合您的 link。
我没有看到 xmlToMapConverter
用法,但我看到了这个:
<int:service-activator
input-channel="fromConvertToActivator-channel"
output-channel="toCallChain-channel"
id="convertRestToWorkflowBean-serviceActivator"
ref="convertRestToWorkflowBean" method="convert">
你在哪里使用它:
<bean id="convertRestToWorkflowBean" class="org.protneut.server.rest.ConvertRestMessageToWorkflowBean" scope="prototype"/>
您遇到的问题是scope impendance
。那是因为 <int:service-activator>
填充了几个 singleton
beans,因此对你的 prototype
的引用也变成了 singleton
。
克服这个问题的一种方法是从那里使用 SpEL:
<int:service-activator
input-channel="fromConvertToActivator-channel"
output-channel="toCallChain-channel"
id="convertRestToWorkflowBean-serviceActivator"
expression="@convertRestToWorkflowBean.convert(payload)"/>
在这种情况下,每次调用时都会从 BeanFactory
中检索您的 convertRestToWorkflowBean
。
继续前进的另一个技巧如下:
<bean id="convertRestToWorkflowBean" class="org.protneut.server.rest.ConvertRestMessageToWorkflowBean" scope="prototype">
<aop:scoped-proxy/>
</bean>
在这种情况下,您的 bean 将被包装到 ScopedProxyFactoryBean
并且所有调用都将根据需要委托给您的 prototype
。