3

初期状況

私の Web アプリケーションは、Maven モジュールmyapp-persistence (.jar)、myapp-model (.jar)、myapp-service (.jar)、およびmyapp-web (.war) で構成され、従来の疎結合の多層構造を取得します。建築。すべてのモジュールは、すべてのサブモジュールの一般的な定義を持つ親POMのみを含む親 Maven モジュールによって結合されます。

特に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は、ビューがこれらのオブジェクトのコンテンツを表示しようとする場合に、コントローラの処理中に開いているトランザクション内にロードされないオブジェクト グラフのインスタンスを遅延ロードできるようにします ( [1]を参照)。よく説明されるように、このフィルターは、展開記述子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 -persistence-context.xmlをインポートするmyapp -servlet.xmlを処理するためと思われます。残念ながら、この推測を参照によって証明することはできません。次の例外がスローされます。

GRAVE: サーブレット [myapp] の Servlet.service() がパス [/myapp] のコンテキストで例外をスローしました
org.springframework.beans.factory.NoSuchBeanDefinitionException: 「sessionFactory」という名前の Bean が定義されていません
    org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:529) で
    org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1095) で
    org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:277) で
    org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) で
    org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1097) で
    org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.lookupSessionFactory(OpenSessionInViewFilter.java:242) で
    org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.lookupSessionFactory(OpenSessionInViewFilter.java:227) で
    org.springframework.orm.hibernate3.support.OpenSessionInViewFilter.doFilterInternal(OpenSessionInViewFilter.java:171) で
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) で
    org.apache.catalina.core.ApplicationFilterChain.internalDoFilter (ApplicationFilterChain.java:243) で
    org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) で
    org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)で
    org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76) で
    org.apache.catalina.core.ApplicationFilterChain.internalDoFilter (ApplicationFilterChain.java:243) で
    org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:210) で
    org.apache.catalina.core.StandardWrapperValve.invoke (StandardWrapperValve.java:222) で
    org.apache.catalina.core.StandardContextValve.invoke (StandardContextValve.java:123) で
    org.apache.catalina.authenticator.AuthenticatorBase.invoke (AuthenticatorBase.java:472) で
    org.apache.catalina.core.StandardHostValve.invoke (StandardHostValve.java:171) で
    org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:99) で
    org.apache.catalina.valves.AccessLogValve.invoke (AccessLogValve.java:936) で
    org.apache.catalina.core.StandardEngineValve.invoke (StandardEngineValve.java:118) で
    org.apache.catalina.connector.CoyoteAdapter.service (CoyoteAdapter.java:407) で
    org.apache.coyote.http11.AbstractHttp11Processor.process(AbstractHttp11Processor.java:1004) で
    org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:589) で
    org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor.run(JIoEndpoint.java:310) で
    java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886) で
    java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908) で
    java.lang.Thread.run(Thread.java:662) で

通常、異なるアプリケーション コンテキスト パーツは、myapp-servlet.xmlではなくContextLoaderListenerを使用してデプロイメント記述子に含まれます。

<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 がデフォルトでビューの遅延読み込みを透過的にサポートしていないのはなぜですか?

予想発言

コンパイル時のプロパティの置換は、ここでは重要ではありません。プロファイル依存プロパティ ファイルは、Maven フィルタリングで既に作成されています。

myapp - persistence ( .jar )とmyapp - service ( _ _ _ _ .jar) - 実際には依存性注入の精神です!

4

2 に答える 2

3

同僚と話すことで解決策が得られましたcontext:property-placeholder。Servlet-API のフィルター メカニズムの代わりに Spring のインターセプターを使用しても、引き続き使用できます。myapp-persistence-context.xmlcontextConfigLocationおよびweb.xmlOpenSessionInViewFilterからの参照を削除し、myapp -persistence-context.xml 内でを宣言しました。OpenSessionInViewInterceptor

<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 Keelingによるプロパティ ファイルの外部化により、プロジェクトのセットアップが完了します。

Spring HandlerInterceptor vs Servlet FiltersおよびSpring docのディスカッションも参照してください。

于 2014-02-12T16:56:22.817 に答える
-1

sessionFactoryで定義されているように、 および非 Web 関連 Bean をルート アプリケーション コンテキストに保持する必要がcontextConfigLocationありweb.xmlます。モジュール性の側面とは別に(あなたが言及したように)、ルートWebアプリケーションコンテキストでを探し、そこで見つからない場合はエラーになるOpenSessionInViewFilterため、このようにする必要があります-あなたが発見したように。したがって、セットアップは正しい方法です。sessionFactorycontextConfigLocation

PropertyPlaceholderConfigurerは、BeanFactoryPostProcessorそれが定義されている Bean ファクトリのコンテキスト内で機能することを意味します。この場合、それは myapp-servlet.xmldataSourceで定義されています。つまり、Web コンテキスト内では機能しますが、ルート アプリケーション コンテキスト (とsolrServerが定義されている場所)ではプレースホルダーを解決しません。

私の提案は<context:property-placeholder>、を Web からルート アプリケーション コンテキストに移動するlocationことです。たとえば、これをmyapp-service-context.xmlに追加できます。

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

そして、ファイルの場所を設定するために、それをmyapp-web.war (または親アプリが何であれ) に任せることができます。たとえば、これはシステム プロパティとして実行できます。

-Dprops.file=file:C:/myapp-configuration.properties
于 2014-01-29T22:24:42.293 に答える