spring-mvc - 是否可以在 Spring 应用程序上下文中配置 OpenSessionInViewFilter,以便上下文 :property-placeholder is useable?

标签 spring-mvc dependency-injection applicationcontext open-session-in-view deployment-descriptor

初步情况

我的 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 定义了一个带有连接变量的数据源:

<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=

这种配置是 imo 直截了当的并且可以工作 - 没有 View 。当 org.springframework.orm.hibernate3.support.OpenSessionInViewFilter 发挥作用时,问题就出现了。

问题描述

OpenSessionInViewFilter 确保在 Controller 处理期间未在打开事务中加载的对象图中的实例可以延迟加载,如果 View 试图显示这些对象的内容(参见 [1])。正如经常描述的那样,这个过滤器在 delpoyment 描述符 web.xml 中声明(参见 [2]):

<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)

Different application context parts are usually included by the deployment descriptor with a ContextLoaderListener and not by 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 的 context:property-placeholder 机制似乎不再起作用了。

目标与问题

像 myapp-persistence(.jar) 和 myapp-service(.jar) 这样的模块必须在运行时通过引用上下文使用属性文件进行配置,例如myapp-web(.war) 的应用程序上下文。

问题是:是否可以在 Spring 应用程序上下文中配置 OpenSessionInViewFilter 以便 context:property-placeholder 仍然可用?

或者:如果应用程序上下文部分包含在部署描述符 web.xml 中,Spring 如何在运行时初始化应用程序上下文中的变量?

从根本上说:为什么实际上必须配置 OpenSessionInViewFilter,为什么 Spring MVC 不透明地支持开箱即用的 View 延迟加载?

预感

编译时的属性替换不是重点。已使用 Maven 过滤创建了配置文件相关属性文件。

将 dataSource 和 solrServer 声明移动到已经提出的 myapp-servlet.xml 中(参见 [3][4] )不是一个可接受的解决方案,因为它破坏了 myapp-persistence(.jar) 和 myapp-service( .jar) - 实际上是依赖注入(inject)的精神!

最佳答案

与同事交谈得到了解决方案:context:property-placeholder如果我使用 Spring 的拦截器而不是 Servlet-API 的过滤器机制,它仍然可以使用。我删除了 contextConfigLocation 中 myapp-persistence-context.xml 的引用和 OpenSessionInViewFilter来自 web.xml 并声明了 OpenSessionInViewInterceptor在 myapp-persistence-context.xml 中:

<mvc:interceptors>
    <bean id="openSessionInViewInterceptor" class="org.springframework.orm.hibernate3.support.OpenSessionInViewInterceptor">
        <property name="sessionFactory" ref="sessionFactory" />
    </bean>
</mvc:interceptors>

如上所述,使用 myapp-persistence(.jar) 的类路径中的 myapp-persistence-context.xml 和 myapp-servlet.xml 中的 import 语句,Spring 在运行时将所有属性变量替换为 myapp-configuration.properties 中的值-按预期时间。模块化被保存在最好的状态! Will Keelingexternalization of the property file完成项目设置。

另见讨论 Spring HandlerInterceptor vs Servlet FiltersSpring doc .

关于spring-mvc - 是否可以在 Spring 应用程序上下文中配置 OpenSessionInViewFilter,以便上下文 :property-placeholder is useable?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21437852/

相关文章:

java - 如何在 PropertySource 中使用相对于用户家的路径

spring - 无法将源 {} 映射到实体

Spring REST错误处理: Cannot have my custom message

java - 如何将工作类注入(inject)到 ServerSocket 实现中?

javascript - 我如何知道可以将哪些依赖项注入(inject)到 Controller 中?

java - Junit Spring 避免加载两次应用程序上下文数据源

java.lang.ExceptionInInitializerError Spring创建Application Context时抛出的异常

java - Spring 异常 org.springframework.beans.factory.BeanCreationException

java - BeanCreationException,Spring 启动

java - 了解配置文件中的主要注释