今天有一個朋友問了我一個問題,他使用的是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只有一個實例,應該說無論初始化多少次都應該獲得的是同一個實例啊?
|
?描述: | 根據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作用的切面就變得難以把握。到現在還無果~~~~
|