今天有一个朋友问了我一个问题,他使用的是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作用的切面就变得难以把握。到现在还无果~~~~
|