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