3

Исходная ситуацияМожно ли настроить контекст приложения OpenSessionInViewFilter в Spring, чтобы контекст: свойство-placeholder можно использовать?

Моя веб-приложение состоит из модулей Maven MyApp-живучесть (.jar), MyApp-модели (.jar), MyApp-служба (.jar) и MyApp -web (.war), чтобы получить традиционную, слабо связанную многоуровневую архитектуру. Все модули объединены родительским модулем Maven, который содержит только родительский POM с общими определениями для всех подмодулей.

Особенно MyApp-служба (.jar) и MyApp-живучесть (.jar) имеет свою собственную конфигурируемую (!) Контекст приложения часть с необходимыми объектами. Обе банки должны быть развернуты с содержащими определениями переменных, другими словами, банки не должны иметь конкретных значений для переменных.

MyApp-сервис-context.xml объявляет solrServer боб с переменной URL сервера:

<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-живучесть-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 он включает их части контекста приложения и предоставляет значения свойств для конфигурации объявленных beans файлом свойства. К 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 гарантирует, что экземпляры в графе объектов, которые не загружены внутри открытой транзакции во время обработки контроллера могут быть загружены лениво, если представление пытается отобразить эти objects' содержания (см. [1]). Как часто описывается, этот фильтр объявляется в дескрипторе delpoyment .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-сохранение-context.xml входит в MYAPP-servlet.xml как выше, так что контекст: свойство-заполнитель работы, 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> 

При такой конфигурации unfourtunately , Spring's контекст: свойство-placeholder механизм, кажется, больше не работает.

Цель и вопрос

модули как MYAPP-настойчивость (.jar) и MyApp-служба (.jar) должен быть настраиваемым во время выполнения с файлом свойств контекста ссылающегося, например контекст приложения myapp-web (.war).

Возникает вопрос: можно ли настроить OpenSessionInViewFilter в контексте приложения Spring, так что контекста: свойство-заполнитель еще полезный?

Или альтернативно: как переменные в контекстах приложения инициализируются Spring во время выполнения, если части контекста приложения включены в дескриптор развертывания web.xml?

По существу: почему на самом деле OpenSessionInViewFilter необходимо настроить, почему Spring MVC не прозрачно поддерживает просмотр ленивой загрузки из коробки?

Предвидя Замечания

замена собственности во время компиляции не точка здесь. Файл с зависимым от профиля профиля уже создан с помощью Maven Filtering.

Перемещение Datasource и solrServer деклараций в MYAPP-servlet.xml Как уже было предложено (см [3], [4]) не является приемлемым решением, потому что она destroyes модульность и независимый тестируемости MYAPP-персистенции ( ,jar) и myapp-service (.jar) - на самом деле дух инъекции зависимостей!

ответ

3

Разговор с коллегой принесло решение: context:property-placeholder остается полезным, если я использую перехватчик вместо стремительности весенних механизма фильтра Servlet-API's. Я удалил ссылку на MYAPP-персистентности-context.xml в contextConfigLocation и OpenSessionInViewFilter от web.xml и объявлен OpenSessionInViewInterceptor в MyApp-сохранение-context.xml:

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

С MyApp -persistence-context.xml в пути к классам myapp-persistence (.jar) и оператор импорта в myapp-servlet.xml, как описано выше, Spring заменяет все переменные свойств значениями в myapp-configuration.properties во время выполнения, как предполагалось. Модульность сохраняется в лучшем виде! Will Keeling's externalization of the property file завершает настройку проекта.

См. Также обсуждение Spring HandlerInterceptor vs Servlet Filters и Spring doc.

+0

Ваш друг показал вам правильный способ обработки сессий спящего режима в любом приложении. Я обычно предпочитаю путь Interceptor, потому что это более безопасный подход, чтобы иметь сложную архитектуру, и только некоторые весенние бобы настраиваются. В настоящее время используйте стиль конфигурации Java, он безопаснее, чем * любая * конфигурация XML. – ThierryB

-1

Обязательно сохраните sessionFactory и не связанные с веб-сайтами компоненты в контексте корневого приложения, как определено contextConfigLocation в web.xml. Помимо аспекта модульности (как вы упомянули), OpenSessionInViewFilter нуждается в этом, потому что он ищет sessionFactory в контексте корневого веб-приложения и it will error if it can't find it there - как вы обнаружили. Таким образом, ваша установка contextConfigLocation - правильный путь.

PropertyPlaceholderConfigurer является BeanFactoryPostProcessor, что означает, что он работает в контексте фабрики фасоли, в которой он определен. В этом случае он определяется в myapp-servlet.xml, что означает, что он будет работать в контексте сети, но не будет разрешать заполнители в контексте корневого приложения (где определены dataSource и solrServer).

Мое предложение было бы переместить <context:property-placeholder> из Интернета в контекст корневого приложения - но параметризуем location, что позволяет установить это приложение. Например, вы могли бы добавить к вашей MYAPP-сервис-context.xml

<context:property-placeholder location="${props.file}"/> 

И тогда вы можете оставить его на MYAPP-web.war (или любой другой родитель приложение случается) для установки местоположения файла. Например, это может быть сделано как свойство системы:

-Dprops.file=file:C:/myapp-configuration.properties 
+0

Это приятное решение, потому что конфигурация полностью экстернализирована. Благодаря! –

Смежные вопросы