初始状况
我的Web应用程序包含Maven模块 myapp-persistence (。jar), myapp-model (。jar), myapp-service (。jar)和 myapp-web (。war)以获得传统的,松散耦合的多层体系结构。所有模块由父Maven模块连接在一起,该模块仅包含父 POM ,其中包含所有子模块的通用定义。
特别是 myapp-service (。jar)和 myapp-persistence (。jar)拥有自己的可配置(!)应用程序上下文部分和所需的对象。两个jar都必须可以使用包含的变量定义进行部署,换句话说,jar不能具有变量的具体值。
myapp-service-context.xml 使用服务器URL的变量声明 solrServer bean:
<bean id="solrServer" class="org.apache.solr.client.solrj.impl.HttpSolrServer">
<constructor-arg value="${solr.serverUrl}" />
<property name="connectionTimeout" value="60000"/>
<property name="defaultMaxConnectionsPerHost" value="40"/>
<property name="maxTotalConnections" value="40"/>
</bean>
myapp-persistence-context.xml 使用连接变量定义 dataSource :
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
</bean>
...
<bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
...
</bean>
myapp-web (。war)引用 myapp-service (。jar)和 myapp-persistence (.jar)。在 myapp-servlet.xml 中,它包含应用程序上下文部分,并提供属性文件配置声明的bean的属性值。通过 context:property-placeholder Spring在内存中创建应用程序上下文时,使用具体值初始化所有变量。
<context:property-placeholder location="classpath*:myapp-configuration.properties" />
<import resource="classpath*:myapp-persistence-context.xml"/>
<import resource="classpath*:myapp-service-context.xml"/>
对于开发配置文件,具体的 myapp-configuration.properties 可能如下所示:
solr.serverUrl=http://localhost:8983/solr
jdbc.dialect=org.hibernate.dialect.HSQLDialect
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:mem:myapp
jdbc.username=sa
jdbc.password=
此配置非常直接且有效 - 无视图。当 org.springframework.orm.hibernate3.support.OpenSessionInViewFilter 发挥作用时,会出现问题。
问题描述
OpenSessionInViewFilter 确保在视图试图显示这些对象的内容时,可以延迟加载对象图中未在打开的事务中加载的实例(如果视图试图显示这些对象的内容)(参见{{3 }})。正如所描述的那样,此过滤器在delpoyment描述符 web.xml 中声明(参见[1]):
<filter>
<filter-name>OpenSessionInViewFilter</filter-name>
<filter-class>org.springframework.orm.hibernate3.support.OpenSessionInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OpenSessionInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
如果 myapp-persistence-context.xml 包含在上面的 myapp-servlet.xml 中,那么 context:property-placeholder 可以正常工作, OpenSessionInViewFilter 找不到必要的 sessionFactory 。原因似乎是Spring首先处理 web.xml 然后 myapp-servlet.xml ,它导入 myapp-persistence-context.xml 。不幸的是,我无法用引用证明这个猜测。抛出以下异常:
GRAVE: Servlet.service() for servlet [myapp] in context with path [/myapp] threw exception org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'sessionFactory' is defined at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:529) at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1095) at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:277) at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1097) at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.lookupSessionFactory(OpenSessionInViewFilter.java:242) at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.lookupSessionFactory(OpenSessionInViewFilter.java:227) at org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:171) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:243) at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:222) at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:123) at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:472) at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:171) at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99) at org.apache.catalina.valves.AccessLogValve.invoke(AccessLogValve.java:936) at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:118) at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:407) at org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004) at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589) at org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310) at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) at java.lang.Thread.run(Thread.java:662)
部署描述符通常包含不同的应用程序上下文部分,其中包含 ContextLoaderListener ,而不是 myapp-servlet.xml :
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
classpath*:myapp-service-context.xml,
classpath*:myapp-persistence-context.xml
</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
有了这种配置,不幸的是,Spring的上下文:property-placeholder 机制似乎不再起作用了。
目标和问题
myapp-persistence (。jar)和 myapp-service (.jar)等模块必须在运行时通过引用上下文与属性文件配置,例如 myapp-web (.war)的应用程序上下文。
问题是:是否可以在Spring应用程序上下文中配置 OpenSessionInViewFilter ,以便 context:property-placeholder 仍然可用?
或者替代方案:如果应用程序上下文部分包含在部署描述符 web.xml 中,Spring如何在运行时初始化应用程序上下文中的变量?
基本上:为什么实际上需要配置 OpenSessionInViewFilter ,为什么Spring MVC不能透明地支持开箱即用的视图延迟加载?
预期备注
编译时的属性替换不是重点。已使用Maven Filtering创建配置文件相关属性文件。
将 dataSource 和 solrServer 声明移植到 myapp-servlet.xml 中(参见[2],{{3} })不是一个可接受的解决方案,因为它破坏了 myapp-persistence (。jar)和 myapp-service (。jar)的模块性和独立可测试性 - 实际上是精神依赖注入!
答案 0 :(得分:3)
与同事交谈带来了解决方案:如果我使用Spring的拦截器而不是Servlet-API的过滤机制,context:property-placeholder
仍然可用。我删除了contextConfigLocation
中 myapp-persistence-context.xml 的引用和 web.xml 中的OpenSessionInViewFilter
,并声明了{{1} } myapp-persistence-context.xml :
OpenSessionInViewInterceptor
myapp-persistence (。jar)的类路径中的 myapp-persistence-context.xml 和 myapp-servlet.xml中的import语句< / em>如上所述,Spring在运行时使用 myapp-configuration.properties 中的值替换所有属性变量。模块化保存最佳! Will Keeling的externalization of the property file完成了项目设置。
另请参阅讨论Spring HandlerInterceptor vs Servlet Filters和Spring doc。
答案 1 :(得分:-1)
您当然应该将sessionFactory
和非与Web相关的bean保留在contextConfigLocation
中web.xml
定义的根应用程序上下文中。除了模块化方面(正如您所提到的),OpenSessionInViewFilter
需要这种方式,因为它在根Web应用程序上下文中查找sessionFactory
并且it will error if it can't find it there - 正如您所发现的那样。因此,contextConfigLocation
设置是正确的方法。
PropertyPlaceholderConfigurer
是BeanFactoryPostProcessor
,这意味着它在定义它的bean工厂的上下文中工作。在这种情况下,它在 myapp-servlet.xml 中定义,这意味着它将在Web上下文中工作,但它不会解析根应用程序上下文中的占位符(dataSource
并且定义了solrServer
。
我的建议是将<context:property-placeholder>
从网络移动到根应用程序上下文 - 但参数化location
允许由封闭的应用程序设置。例如,您可以将其添加到 myapp-service-context.xml
<context:property-placeholder location="${props.file}"/>
然后您可以将其留给 myapp-web.war (或者无论父应用程序是什么)来设置文件的位置。例如,这可以作为系统属性完成:
-Dprops.file=file:C:/myapp-configuration.properties