今天有一個朋友問了我一個問題,他使用的是Hibernate/Spring/Struts架構,配置使用Spring的OpenSessionInView Filter,但是發現不生效,lazy的集合屬性在頁面訪問的時候仍然報session已經關閉的錯誤。我和他一起檢查了所有的配置和相關的代碼,但是沒有發現任何問題。經過調試發現,應用程序使用的Session和OpenSessionInView Filter打開的Session不是同一個,所以OpenSessionInView模式沒有生效,但是為什么他們不使用同一個Session呢?
檢查了一遍Spring的相關源代碼,發現了問題的根源:
通常在Web應用中初始化Spring的配置,我們會在web.xml里面配置一個Listener,即:?
???
xml代碼:?
<listener>?
?? <listener-class>
???? org.springframework.web.context.ContextLoaderListener
?? </listener-class>?
</listener>
如果使用Struts,那么需要在Struts的配置文件struts-config.xml里面配置一個Spring的plugin:ContextLoaderPlugIn。

實際上ContextLoaderListener和ContextLoaderPlugIn的功能是重疊的,他們都是進行Spring配置的初始化工作的。因此,如果你不打算使用OpenSessionInView,那么你并不需要在web.xml里面配置ContextLoaderListener。

好了,但是你現在既需要Struts集成Spring,又需要OpenSessionInView模式,問題就來了!

由于ContextLoaderListener和ContextLoaderPlugIn功能重疊,都是初始化Spring,你不應該進行兩次初始化,所以你不應該同時使用這兩者,只能選擇一個,因為你現在需要集成Struts,所以你只能使用ContextLoaderPlugIn。

但是令人困惑的是,ContextLoaderListener和ContextLoaderPlugIn有一個非常矛盾的地方!

ContextLoaderListener初始化spring配置,然后把它放在ServletContext對象里面保存:

java代碼:?

servletContext.setAttribute(
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);

請注意,保存的對象的key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE!
但是ContextLoaderPlugIn初始化spring配置,然后把它放在ServletContext對象里面保存:

java代碼:?


String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);

這個attrName和WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE名字是不一樣的!

如果僅僅是名字不一樣,問題還不大,你仍然可以放心使用ContextLoaderPlugIn,但是當你使用OpenSessionInView的時候,OpenSessionInViewFilter是使用哪個key取得spring配置的呢?

java代碼:?

WebApplicationContext wac =
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());


顯然,OpenSessionInViewFilter是按照WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE這個key去拿spring配置的!

我們整理一下思路:

ContextLoaderPlugIn保存spring配置的名字叫做attrName;
,ContextLoaderListener保存spring配置的名字叫做WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE;
而OpenSessionInView是按照WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE這個名字去取得spring配置的!
而你的應用程序卻是按照attrName去取得spring的配置的!

所以,OpenSessionInView模式失效!

解決辦法:
修改ContextLoaderPlugIn代碼,在getServletContext().setAttribute(attrName, wac);這個地方加上一行代碼:
getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac);

或者修改OpenSessionInViewFilter,讓它按照attrName去取得spring配置。



我原來用struts/spring/hibernate的時候同樣使用OpenSessionInView,但是似乎沒有robbin所說的問題啊。而且我在使用的時候,是ContextLoaderListener和ContextLoaderPlugIn一起用的。整個配置如下:
1.首先是web.xml
java代碼:?


? ? ? ? <filter>
? ? ? ? <filter-name>OpenSessionInViewFilter</filter-name>
? ? ? ? <filter-class>org.springframework.orm.hibernate.support.OpenSessionInViewFilter</filter-class>
? ? </filter>
? ?
? ? <filter-mapping>
? ? ? ? <filter-name>OpenSessionInViewFilter</filter-name>
? ? ? ? <url-pattern>/*</url-pattern>
? ? </filter-mapping>
? ?
? ? <listener>
? ? ? ? ? ? ? ? <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
? ? ? ? </listener>

......



2. 然后是struts-config.xml:
java代碼:?


<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
? ? ? ? <set-property property="contextConfigLocation"
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? value="/WEB-INF/action-servlet.xml"
? ? ? ? />
</plug-in>



其余部分省略。

在上述配置下,使用OpenSessionInView似乎沒有問題。

不知道robbin所說的ContextLoaderListener和ContextLoaderPlugIn不應該同時使用是不是做得是如下的配置:(struts-config.xml)

java代碼:?


<plug-in
className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/applicationContext.xml,
/WEB-INF/action-servlet.xml"/>
</plug-in>



我嘗試了一下,用這種配置時,OpenSessionInView的確失效了。

我猜想,原因大概是這樣:struts的這個plugIn,可能只是為了整合一個action-servlet.xml,將action-servlet.xml中的定義當作Spring的bean來使用,因此,在保存時,只要有action-servlet.xml的配置,就被保存到robbin所提到的那個attrName中,而不是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE中,所以,OpenSessionInView是取不到這個配置的。

那么這個配置什么時候被取到呢?直覺告訴我,可能是和Action的Proxy有關。于是,查看了org.springframework.web.struts.DelegatingActionProxy的源碼,果然:
java代碼:?


/**
? ? ? ? * Return the delegate Action for the given mapping.
? ? ? ? * <p>The default implementation determines a bean name from the
? ? ? ? * given ActionMapping and looks up the corresponding bean in the
? ? ? ? * WebApplicationContext.
? ? ? ? * @param mapping the Struts ActionMapping
? ? ? ? * @return the delegate Action
? ? ? ? * @throws BeansException if thrown by WebApplicationContext methods
? ? ? ? * @see #determineActionBeanName
? ? ? ? */

? ? ? ? protectedAction getDelegateAction(ActionMapping mapping)throws BeansException {
? ? ? ? ? ? ? ? WebApplicationContext wac = getWebApplicationContext(getServlet(), mapping.getModuleConfig());
? ? ? ? ? ? ? ? String beanName = determineActionBeanName(mapping);
? ? ? ? ? ? ? ? return(Action) wac.getBean(beanName, Action.class);
? ? ? ? }

? ? ? ? /**
? ? ? ? * Fetch ContextLoaderPlugIn's WebApplicationContext from the
? ? ? ? * ServletContext, containing the Struts Action beans to delegate to.
? ? ? ? * @param actionServlet the associated ActionServlet
? ? ? ? * @param moduleConfig the associated ModuleConfig
? ? ? ? * @return the WebApplicationContext
? ? ? ? * @throws IllegalStateException if no WebApplicationContext could be found
? ? ? ? * @see DelegatingActionUtils#getRequiredWebApplicationContext
? ? ? ? * @see ContextLoaderPlugIn#SERVLET_CONTEXT_PREFIX
? ? ? ? */

? ? ? ? protected WebApplicationContext getWebApplicationContext(
? ? ? ? ? ? ? ? ? ? ? ? ActionServlet actionServlet, ModuleConfig moduleConfig)throwsIllegalStateException{
? ? ? ? ? ? ? ? return DelegatingActionUtils.getRequiredWebApplicationContext(actionServlet, moduleConfig);
? ? ? ? }



仔細看其中的取wac的代碼,它并不是從WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE取的wac。

由此,我相信,除了robbin講的修改源碼以外,同時使用ContextLoaderListener和ContextLoaderPlugIn,但是不要在ContextLoaderPlugIn里面加入applicationContext.xml,只要加入你的action-servlet.xml,我相信,同樣也可以非常流暢的使用OpenSessionInView



我也遇到了上面說的openSessionInView不起作用的問題(web.xml既定義了listener,也定義了struts plugin),我想問一下,上面提到的action-servlet.xml到底是什么內容?
在我的應用里spring的配置文件是application-context.xml,它本身是空的,引用spring-data.xml,sping-security.xml等等和存放對應struts action的spring 配置文件spring-struts-action.xml。
struts的配置文件是struts-config.xml,里面定義了所有的action,它們的class都是org.springframework.web.struts.DelegatingActionProxy。最后的plug-in是
java代碼:?


<plug-in
className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/applicationContext.xml"/>
</plug-in>



結果也遇到了openSessionInView不起作用的問題
在我的應用里都沒有出現過action-servlet.xml,我想問下它到底是什么?是對應于我的spring-struts-action.xml還是struts-config.xml引用的一部分?

通俗的說,這個action-servlet.xml到底是spring配置文件還是struts的配置文件?







我仔細想了一下,那個action-servlet.xml應該是spring配置的一部分,也就是說對應我的spring-struts-action.xml(明確的說,這個里面的xml語法是spring配置文件的),應該是這樣的吧?不過按照這個理解下去,我又產生了問題。
我的理解時這樣的,spring里面的listener會在web.xml里加載spring的容器,struts ActionServlet初始化時又會根據struts-config.xml里的spring plugin配置再初始化一個spring容器,所以原則上說只要一個就可以了,如果2處都配了,會初始化2個spring容器,在和struts結合的用法里,實際有效的是stuts配置里面那個plugin初始化的容器,因為用戶操作的入口都是struts的action。那么二樓提供的方法其實就是所有的bean都由那個listener初始化的,存在于第一個spring容器中,然后stuts只初始化那些和struts action關聯的action bean,存在第二個容器里(這兩個容器的區分就在于robbin提到的他們的名字不同)但是問題就是:
為什么在二樓的的方法中,用戶通過action訪問spring bean,那么應該只是訪問的第二個容器里的action bean,而service bean在第一個容器里,那第二個容器里的action bean是怎么會可以訪問到第一個容器里的service bean和其他所有spring bean的呢?實在是費解








感謝摟主的分析,spring的struts plugin確實有上述描述的問題
如果根據原來的方法,context會初始化2次,看了plugin的源碼以后我對它作了小小的修改,首先檢查context是不是被初始化過,如果有則直接從attribute中獲取,如果沒有初始化,則根據plugin的配置初始化,同時保證了context只被初始化一次。
原來的意圖是屏蔽web.xml中的context監聽,直接用plugin初始化context,但啟動失敗,于是作了上述修改
java代碼:?


//read the application context from the aplication attribute
? ? ? ? ? ? ? ? WebApplicationContext wac = null;
? ? ? ? ? ? ? ? String attrName = getServletContextAttributeName();
? ? ? ? ? ? ? ? if(getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE)!=null){? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? wac = (WebApplicationContext) getServletContext().getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
? ? ? ? ? ? ? ? ? ? ? ? logger.info("Using the context listener's context "+wac);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? else {
? ? ? ? ? ? ? ? ? ? ? ? logger.info("Load the context plugin application context ");
? ? ? ? ? ? ? ? ? ? ? ? WebApplicationContext parent = WebApplicationContextUtils.getWebApplicationContext(getServletContext());

? ? ? ? ? ? ? ? ? ? ? ? wac = createWebApplicationContext(parent);
? ? ? ? ? ? ? ? ? ? ? ? if (logger.isInfoEnabled()) {
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? logger.info("Using context class '
" + wac.getClass().getName() + "' for servlet '" + getServletName() + "'");
? ? ? ? ? ? ? ? ? ? ? ? }? ? ? ? ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? ? ? ? ? //set to attribute to spring listener
? ? ? ? ? ? ? ? ? ? ? ? getServletContext().setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, wac); ? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ?
? ? ? ? ? ? ? ? // Publish the context as a servlet context attribute.
? ? ? ? ? ? ? ? getServletContext().setAttribute(attrName, wac);



PS. 有個疑問,如果說spirng中的bean只有一個實例,應該說無論初始化多少次都應該獲得的是同一個實例啊?



strutspugin.rar
?描述:
根據spirng 1.2.7 重新編譯的ContextLoaderPlugin

下載
?文件名:?strutspugin.rar
?文件大小:?4.16 KB
?下載過的:?文件被下載或查看 194 次






我覺得這個根本就是大家對Spring的理解的問題。

如果這真是一個嚴重的問題,以至于需要修改源碼來修正,Spring的team不會到現在沒有發現,到現在還沒有修正。為什么Spring的context分成了多個文件?為什么用applicationContext.xml了,還有xxx-servlet.xml?


如果大家監聽ContextRefreshedEvent的話,會發現一個web app至少會有兩個這樣的event,下面是我的現在的應用打印出的context及其所包含的beans:
org.springframework.web.context.support.XmlWebApplicationContext: display name [Root WebApplicationContext]; startup date
[Wed May 10 17:30:13 CST 2006]; child of [org.springframework.context.support.ClassPathXmlApplicationContext: display name [org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=3736840]; startup date [Wed May 10 17:30:09 CST
2006]; root of context hierarchy]; config locations
[/WEB-INF/webApplicationContext.xml,/WEB-INF/webApplicationContext-*.xml,/WEB-INF/standardJspApplicationContext.xml,classpath
*:config/spring/app-context-base.xml,classpath*:config/spring/app-context-hibernate.xml,classpath*:config/spring/app-context-
ibatis.xml,classpath*:config/spring/app-context-integration.xml,classpath*:config/spring/app-context-biz.xml]

[messageSource, localeResolver, exposeSpringBeanDefinition, dataListOfTerminalInfoForm, dataListOfPointsSpecialOfferForm,
dataListOfSearchTerminalForm, pointsSpecialOfferForm1, terminalInfoForm1, searchTerminalForm1,
dataListOfSearchCardAccountDetailForm, dataListOfSearchPhysicalCardInfoForm, dataListOfSearchCardApplicationInfoForm,
dataListOfSearchTransactionForm, searchCardAccountDetailForm1, searchCardAccountDetailForm2, searchCardAccountDetailForm3,
searchPhysicalCardInfoForm1, searchPhysicalCardInfoForm2, searchPhysicalCardInfoForm3, searchTransactionForm1, searchTransactionForm2, searchTransactionForm3, displayTransactionFormForTest, dataListOfReplaceCardForm, dataListOfSearchCardInfoForm, cardInfoFormForTest, cardAccountForm1, cardAccountForm2, cardAccountForm3, searchCardInfoForm1, searchCardInfoForm2, searchCardInfoForm3, replaceCardForm1, replaceCardForm2, replaceCardForm3,
searchCardApplicationInfoForm1, searchCardApplicationInfoForm2, searchCardApplicationInfoForm3,
displayCardApplicationInfoFormForTest, displayCardApplicationInfoFormForTest1, csvDisplayProvider, excelDisplayProvider,
classicLook, simpleLook, microsoftLook, dacHandler, integer0, integer1, integer2, integer3, integer4, integer5, integer6, integer7, integer8, integer9, sessionFactory, transactionManager, hibernateTemplate, abstractHibernateDao, abstractDacHolderHibernateDao, ageLevelDefinitionDao, auditLogDao, bankDao, bankBranchDao, binRangeDao, cardDao,
cardAccountDao, cardAccountDetailDao, cardApplicationDao, cardSalesAgentDao, cardTypeDefinitionDao, centerDao,
centerAccountDao, centerAccountDetailDao, corporationDao, corporationTypeDefinitionDao, csaAccountDao, csaAccountDetailDao, csaBillsDao, csaTypeDefinitionDao, educationLevelDefinitionDao, generalLedgerDao, generalLedgerDetailDao, generalLedgerTypeDefinitionDao, identificationTypeDefinitionDao, incomeLevelDefinitionDao, industryDao, journalDao, journalBackupDao, maritalStatusDefinitionDao, merchantDao, merchantAccountDao, merchantAccountDetailDao, merchantApplicationDao, merchantBillsDao, merchantTypeDefinitionDao, occupationTypeDefinitionDao, outboundEmailDao, permissionDao, physicalCardDao, pointsSpecialOfferDao, residentialTypeDefinitionDao, roleDefinitionDao, rolePermissionDao,
systemConfigDao, terminalDao, terminalConfigurationDao, terminalModelDefinitionDao, transactionSummaryDao,
transactionTypeDefinitionDao, userDao, userCreditRatingDao, userLevelDefinitionDao, userRoleDao, sqlMapClient,
abstractIbatisValueListAdapter, valueListHandler, propertyConfigurer, dataSource, voidTransactionTemplate,
inquiryBalanceTransactionTemplate, definitionBizFacade, facadeHolder, pointsTransactionTemplate, emailBizObject,
clsSpringEventListener, balanceBizFacadeTarget, kernelBizObject, printBizFacadeTarget, pointsSpecialOfferBizFacadeTarget,
settlementBizObject, addPointsTransactionTemplate, inquiryMerchantAccountInfoTransactionTemplate,
addMerchantPointsTransactionTemplate, emailBizFacadeTarget, centerBizFacadeTarget, monitorBizFacadeTarget,
endOfDayReportTransactionTemplate, cardBizFacadeTarget, pointsCalculator, balanceBizObject, pointsSpecialOfferBizObject,
auditLogBizFacadeTarget, terminalBizFacadeTarget, terminalBizObject, templateHolder, settlementBizFacadeTarget,
merchantBizObject, userBizObject, changePinTransactionTemplate, centerPurchasePointsBackTransactionTemplate,
definitionBizObject, monitorBizObject, auditLogBizObject, merchantBizFacadeTarget, userBizFacadeTarget,
responseMessageDataFactoryBean, tradingBizObject, printBizObject, csaBizObject, csaBizFacadeTarget, kernelBizFacadeTarget,
cardBizObject, centerBizObject, tradingBizFacadeTarget, downloadParametersTransactionTemplate, baseTransactionProxy,
abstractDataFacade, balanceBizFacade, printBizFacade, settlementBizFacade, emailBizFacade, kernelBizFacade,
auditLogBizFacade, pointsSpecialOfferBizFacade, terminalBizFacade, userBizFacade, merchantBizFacade, csaBizFacade,
monitorBizFacade, cardBizFacade, centerBizFacade, tradingBizFacade, pointsConverter, transactionTypeHelperBean, integer100, integer106, integer110, integer111, integer120, integer180, integer181, integer182, integer200, integer220, integer221]



---------------------------------------------------------------context2
org.springframework.web.context.support.XmlWebApplicationContext: display name [WebApplicationContext for namespace
'action-servlet']; startup date [Wed May 10 17:31:01 CST 2006]; child of
[org.springframework.web.context.support.XmlWebApplicationContext: display name [Root WebApplicationContext]; startup date
[Wed May 10 17:30:13 CST 2006]; child of [org.springframework.context.support.ClassPathXmlApplicationContext: display name
[org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=3736840]; startup date [Wed May 10 17:30:09 CST
2006]; root of context hierarchy]; config locations
[/WEB-INF/webApplicationContext.xml,/WEB-INF/webApplicationContext-*.xml,/WEB-INF/standardJspApplicationContext.xml,classpath
*:config/spring/app-context-base.xml,classpath*:config/spring/app-context-hibernate.xml,classpath*:config/spring/app-context-
ibatis.xml,classpath*:config/spring/app-context-integration.xml,classpath*:config/spring/app-context-biz.xml]]; config
locations [/WEB-INF/action-servlet.xml]

[/EditCurrentUserInfoAction, /FakeLoginAction, /SaveCardInfoAction, /LoginAction, /EditOperatorPswAction,
/SavePointsSpecialOfferAction, /DisplayCurrentUserInfoAction, /DisplayOperatorApplicationAction, /EditEmailAction,
/RegisterCardAction, /IssueCardsAction, /SearchEmailAction, /RegisterCsaAction, /EditTerminalInfoAction,
/ReleaseTerminalAction, /ReleaseCsaAction, /SaveTerminalInfoAction, /SearchTransactionAction, /SearchOperatorInfoAction,
/ProcessCardApplicationAction, /EditTerminalConfigurationAction, /EditGenericUserByIdAction, /SearchMerchantInfoAction,
/SearchTerminalAction, /SaveCsaPswAction, /SaveCsaInfoAction, /SaveCardTypeAction, /RegisterPointsSpecialOfferAction,
/SearchCardInfoAction, /EditMerchantInfoAction, /SearchCardAccountDetailAction, /SearchCardApplicationInfoAction,
/DisplayTransactionStatisticsAction, /DisplayRegisterCardInfoAction, /SaveEmailAction, /EditCardInfoByIdAction,
/MoniterSystemLogAction, /ReleasePointsSpecialOfferAction, /SearchMerchantAccountDetailAction, /EditMerchantPswAction,
/ReleaseMerchantAction, /ListCardTypeAction, /StockCardsAction, /ProcessOperatorApplicationAction,
/SearchPhysicalCardInfoAction, /SearchCsaInfoAction, /SearchOperatorApplicationAction, /ReleaseOperatorAction,
/DisplayCardApplicationInfoAction, searchEmailValueListBuilder, /SearchCsaApplicationAction, /RegisterCardTypeAction,
/MonitorTerminalStatusAction, /SearchCsaAccountDetailAction, /SearchUserAction, /ReleaseCardTypeAction, /ReleaseUserAction,
/ReleaseEmailAction, /CreateBlankCardsAction, /RegisterBulkCardsAction, /SaveMerchantPswAction,
/SearchPointsSpecialOfferAction, /EditPswAction, /SearchMerchantApplicationAction, /DisplayCsaApplicationAction,
/EndOfDayAction, /EditPointsSpecialOfferAction, /DisplayMerchantApplicationAction, /RegisterEmailAction, /EditCsaPswAction,
/ProcessMerchantApplicationAction, /EditCardTypeDefinitionAction, /SaveOperatorInfoAction, /SaveMerchantInfoAction,
/SaveOperatorPswAction, /EditOperatorInfoAction, /RegisterMerchantAction, /EditCsaInfoAction, /ChangeSystemStatusAction,
/ProcessSystemLogAction, /RegisterOperatorAction, /RegisterTerminalAction, /DisplayTransanctionAction,
/ProcessCsaApplicationAction, /MerchantPointsRedeemTransactionReportAction, /SaveTerminalConfigurationAction,
autoProxyCreator, profilingAdvice, profilingAdvisor, strutsActionAdvice, baseSearchAction, userTypeBasedSelector,
valueListBuilder]



------------------------------------------------------------------------context3
org.springframework.web.context.support.XmlWebApplicationContext: display name [WebApplicationContext for namespace
'trading-servlet']; startup date [Wed May 10 17:31:08 CST 2006]; child of
[org.springframework.web.context.support.XmlWebApplicationContext: display name [Root WebApplicationContext]; startup date
[Wed May 10 17:30:13 CST 2006]; child of [org.springframework.context.support.ClassPathXmlApplicationContext: display name
[org.springframework.context.support.ClassPathXmlApplicationContext;hashCode=3736840]; startup date [Wed May 10 17:30:09 CST
2006]; root of context hierarchy]; config locations
[/WEB-INF/webApplicationContext.xml,/WEB-INF/webApplicationContext-*.xml,/WEB-INF/standardJspApplicationContext.xml,classpath
*:config/spring/app-context-base.xml,classpath*:config/spring/app-context-hibernate.xml,classpath*:config/spring/app-context-
ibatis.xml,classpath*:config/spring/app-context-integration.xml,classpath*:config/spring/app-context-biz.xml]]; config locations [/WEB-INF/trading-servlet.xml]
[clsRawTagElementParser, transactionProcessor, clsTradingServlet, rawTagElementParser]



如果照大家所說的方法去改源代碼,那么后啟動的servlet的context會覆蓋前面一個啟動的servlet的context,對于我的應用來說,那種方式會導致action-servlet丟失。開始robbin提出的錯誤,是因為你在strutsPlugin里多配置了appContext,導致實際上有2分appContext的beans存在,child在自己的context里就可以找到所需要的bean,也就不會去parent里找了。StrutsPlugin里的attrName是正確合理的。

當然你可以把所有所有的bean全部放到root context里,這也行的通,不過本人極力反對這種方式,bean的組織太亂。

Spring的context是分層次的:不要把在寫contextConfigLocation的時候,把你的xxx-servlet.xml路徑也加進去;不要在寫xxx-servlet.xml的context的時候把applicationContext的路徑也配進去;不要在parent的context里引用children里的bean,不要在你的appContext里引用xxx-servlet的bean。

總之,就是要求你合理的、有層次的組織你的bena,而不是一陀擺出來。





applicationContext.xml如果不引用action-servlet.xml路徑的話,那么action如何來引用bo;
java代碼:?


<bean name="/test" class="com.xy.action.TestAction">
? <property name="testBo"><ref bean="testBoProxy"/></property>
</bean>



如果bo在applicationContext.xml中的話;
服務器會報錯,找不到bo





okokok 寫道:
applicationContext.xml如果不引用action-servlet.xml路徑的話,那么action如何來引用bo;
java代碼:?


<bean name="/test" class="com.xy.action.TestAction">
? <property name="testBo"><ref bean="testBoProxy"/></property>
</bean>



如果bo在applicationContext.xml中的話;
服務器會報錯,找不到bo


我不太清楚你的bean的組織,在我的系統里,BO是在applicationContext之類的基礎context里定義,而且工作很正常。

另外你需要搞清楚的是:對于Spring的BeanFactory(ApplicationContext),如果它在自己的context里找不到bean,會去parent里找。

java代碼:?


// Check if bean definition exists in this factory.
? ? ? ? ? ? ? ? ? ? ? ? if(getParentBeanFactory() != null && !containsBeanDefinition(beanName)){
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // Not found -> check parent.
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? if(getParentBeanFactory() instanceof AbstractBeanFactory){
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // Delegation to parent with args only possible for AbstractBeanFactory.
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return((AbstractBeanFactory) getParentBeanFactory()).getBean(name, requiredType, args);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? elseif(args == null){
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? // No args -> delegate to standard getBean method.
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? return getParentBeanFactory().getBean(name, requiredType);
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? else{
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? throw new NoSuchBeanDefinitionException(beanName,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? "Cannot delegate to parent BeanFactory because it does not supported passed-in arguments");
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? }



所以無論如何,只要你在applicationContext里定義了BO,那么webApp的context一定找得到這個bean,因為applicationContext是webApp的context的parent。






奇了怪了,昨天一直報找不到bo的錯,今天居然沒報錯;服務器有問題?
還有個問題,既然web.xml里可以用listener來加載applicationContext.xml,為什么還要在struts-config.xml里再用plug-in?我覺得在applicationContext.xml里按模塊放置每個模塊的action,bo,dao的xml文件的路徑是個不錯方法,比如:
java代碼:?


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
? ? ? ? <import resource="SpringConfig/module1.xml" />
? ? ? ? ? ? ? ? <import resource="SpringConfig/module2.xml" />
? ? ? ? ? ? ? ? <import resource="SpringConfig/module3.xml" />

? ? ? ? <bean id="dataSource"
? ? ? ? ? ? ? ? class="org.apache.commons.dbcp.BasicDataSource">
? ? ? ? ? ? ? ? <property name="driverClassName">
? ? ? ? ? ? ? ? ? ? ? ? <value>org.gjt.mm.mysql.Driver</value>
? ? ? ? ? ? ? ? </property>
? ? ? ? ? ? ? ? <property name="url">
? ? ? ? ? ? ? ? ? ? ? ? <value>jdbc:mysql://localhost/airline</value>
? ? ? ? ? ? ? ? </property>
? ? ? ? ? ? ? ? <property name="username">
? ? ? ? ? ? ? ? ? ? ? ? <value>root</value>
? ? ? ? ? ? ? ? </property>
? ? ? ? ? ? ? ? <property name="password">
? ? ? ? ? ? ? ? ? ? ? ? <value>123456</value>
? ? ? ? ? ? ? ? </property>
? ? ? ? </bean>
? ? ? ? ?...
</beans>


而module1.xml的內容是:

java代碼:?


?<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<!-- 數據訪問層 -->
<bean id="testDao" class="com.xy.dao.TestDao">
? <property name="sessionFactory"><ref bean="sessionFactory"/></property>
</bean>
<!-- 業務羅基層 -->
<bean id="testBo" class="com.xy.bo.TestBo">
? <property name="testDao"><ref bean="testDao"/></property>
? <!-- <property name="transactionManager"><ref bean="transactionManager"/></property> -->
</bean>
<!-- Action層 -->
<bean name="/test" class="com.xy.action.TestAction">
? <property name="testBo"><ref bean="testBoProxy"/></property>
</bean>
</beans>


這樣的話不用在struts-config.xml里配置plug-in了吧





struts-config.xml里配置plug-in是要配的,關鍵在于你Spring的配置文件的合理分層,如果像你那樣什么東西都放在一個applictionContext里,那么就有可能出現象這樣OpenSessioInView失效的這樣“意想不到”的問題。而且會導致你錯誤地理解Spring,比如Spring的ApplicationEvent,本身web層的context里的Listener是聽不到root層的Event的,但是你這樣的配置(也是這個topic的配置)會導致Event混淆,也違背了Spring本身的設計意圖。

PS:關于Event的問題可以看我blog,http://spaces.msn.com/sweetriver/blog/cns!367370EB9A9B2807!129.entry

在我的配置里,各個層次都有屬于自己的配置文件,messageSource同樣應該有分層,為了圖方便而簡單吧所有bean羅列在一個配置里是不可取的,而且會導致某些設計與實現上的問題(開始我的messageSource是沒有分層的,但是后來這樣導致了一些非常痛苦的問題與抉擇,結果還是改回分層的messageSource)。





applicationContext是有層次的,那樣的方式會讓struts plugIn中配置的sessionFactory比root中的sessionFactory占優,這個struts plugIn中的bean都是訪問這里配置的sessionFactory。



opensession的最大問題還是長鏈接的時候無法釋放session的問題,在對外系統中問題尤為突出。springMVC可以仗著先天優勢用interecptor,但是webwork可就沒有這樣的優勢了,嘗試自己做一個適合webwork的,但是發現一旦有安全框架介入,session作用的切面就變得難以把握。到現在還無果~~~~