论坛首页 Java版 Struts

Hibernate/Spring/Struts架构使用OpenSessionInView的问题

浏览 37983 次
该帖已经被评为精华帖
作者 正文
时间:2005-08-03
今天有一个朋友问了我一个问题,他使用的是Hibernate/Spring/Struts架构,配置使用Spring的OpenSessionInView Filter,但是发现不生效,lazy的集合属性在页面访问的时候仍然报session已经关闭的错误。我和他一起检查了所有的配置和相关的代码,但是没有发现任何问题。经过调试发现,应用程序使用的Session和OpenSessionInView Filter打开的Session不是同一个,所以OpenSessionInView模式没有生效,但是为什么他们不使用同一个Session呢?

检查了一遍Spring的相关源代码,发现了问题的根源:

通常在Web应用中初始化Spring的配置,我们会在web.xml里面配置一个Listener,即:

	<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对象里面保存:

[code:1]servletContext.setAttribute(
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);[/code:1]

请注意,保存的对象的key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE!

但是ContextLoaderPlugIn初始化spring配置,然后把它放在ServletContext对象里面保存:

[code:1]
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);[/code:1]

这个attrName和WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE名字是不一样的!

如果仅仅是名字不一样,问题还不大,你仍然可以放心使用ContextLoaderPlugIn,但是当你使用OpenSessionInView的时候,OpenSessionInViewFilter是使用哪个key取得spring配置的呢?

[code:1]WebApplicationContext wac =
WebApplicationContextUtils.getRequiredWebApplicationContext(getServletContext());[/code:1]

显然,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配置。
   
时间:2005-08-04
我原来用struts/spring/hibernate的时候同样使用OpenSessionInView,但是似乎没有robbin所说的问题啊。而且我在使用的时候,是ContextLoaderListener和ContextLoaderPlugIn一起用的。整个配置如下:
1.首先是web.xml
[code:1]
<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>

......
[/code:1]

2. 然后是struts-config.xml:
[code:1]
<plug-in className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/action-servlet.xml"
/>
</plug-in>
[/code:1]

其余部分省略。

在上述配置下,使用OpenSessionInView似乎没有问题。

不知道robbin所说的ContextLoaderListener和ContextLoaderPlugIn不应该同时使用是不是做得是如下的配置:(struts-config.xml)

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

我尝试了一下,用这种配置时,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的源码,果然:
[code:1]
/**
* 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
*/
protected Action 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) throws IllegalStateException {
return DelegatingActionUtils.getRequiredWebApplicationContext(actionServlet, moduleConfig);
}
[/code:1]

仔细看其中的取wac的代码,它并不是从WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE取的wac。

由此,我相信,除了robbin讲的修改源码以外,同时使用ContextLoaderListener和ContextLoaderPlugIn,但是不要在ContextLoaderPlugIn里面加入applicationContext.xml,只要加入你的action-servlet.xml,我相信,同样也可以非常流畅的使用OpenSessionInView
   
0 请登录后投票
时间:2005-08-04
有道理,应该是这样的。
   
0 请登录后投票
时间:2005-12-16
我也遇到了上面说的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是
[code:1]
<plug-in
className="org.springframework.web.struts.ContextLoaderPlugIn">
<set-property property="contextConfigLocation"
value="/WEB-INF/applicationContext.xml"/>
</plug-in>
[/code:1]

结果也遇到了openSessionInView不起作用的问题
在我的应用里都没有出现过action-servlet.xml,我想问下它到底是什么?是对应于我的spring-struts-action.xml还是struts-config.xml引用的一部分?

通俗的说,这个action-servlet.xml到底是spring配置文件还是struts的配置文件?
   
0 请登录后投票
时间:2005-12-16
我仔细想了一下,那个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的呢?实在是费解
   
0 请登录后投票
时间:2006-03-27
感谢搂主的分析,spring的struts plugin确实有上述描述的问题
如果根据原来的方法,context会初始化2次,看了plugin的源码以后我对它作了小小的修改,首先检查context是不是被初始化过,如果有则直接从attribute中获取,如果没有初始化,则根据plugin的配置初始化,同时保证了context只被初始化一次。
原来的意图是屏蔽web.xml中的context监听,直接用plugin初始化context,但启动失败,于是作了上述修改
[code:1]
//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);
[/code:1]

PS. 有个疑问,如果说spirng中的bean只有一个实例,应该说无论初始化多少次都应该获得的是同一个实例啊?
  • strutspugin.rar (4.2 KB)
  • 描述: 根据spirng 1.2.7 重新编译的ContextLoaderPlugin
  • 下载次数: 405
   
0 请登录后投票
时间:2006-05-10
我觉得这个根本就是大家对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,而不是一陀摆出来。
   
0 请登录后投票
时间:2006-05-30
applicationContext.xml如果不引用action-servlet.xml路径的话,那么action如何来引用bo;
[code:1]
<bean name="/test" class="com.xy.action.TestAction">
<property name="testBo"><ref bean="testBoProxy"/></property>
</bean>
[/code:1]

如果bo在applicationContext.xml中的话;
服务器会报错,找不到bo
   
0 请登录后投票
时间:2006-05-30
okokok 写道
applicationContext.xml如果不引用action-servlet.xml路径的话,那么action如何来引用bo;
[code:1]
<bean name="/test" class="com.xy.action.TestAction">
<property name="testBo"><ref bean="testBoProxy"/></property>
</bean>
[/code:1]

如果bo在applicationContext.xml中的话;
服务器会报错,找不到bo


我不太清楚你的bean的组织,在我的系统里,BO是在applicationContext之类的基础context里定义,而且工作很正常。

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

[code:1]
// 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);
}
else if (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");
}
}
[/code:1]

所以无论如何,只要你在applicationContext里定义了BO,那么webApp的context一定找得到这个bean,因为applicationContext是webApp的context的parent。
   
0 请登录后投票
时间:2006-05-31
奇了怪了,昨天一直报找不到bo的错,今天居然没报错;服务器有问题?
还有个问题,既然web.xml里可以用listener来加载applicationContext.xml,为什么还要在struts-config.xml里再用plug-in?我觉得在applicationContext.xml里按模块放置每个模块的action,bo,dao的xml文件的路径是个不错方法,比如:
[code:1]
<?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>
[/code:1]
而module1.xml的内容是:

[code:1]
<?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>
[/code:1]
这样的话不用在struts-config.xml里配置plug-in了吧
   
0 请登录后投票
论坛首页 Java版 Struts

跳转论坛:
JavaEye推荐