为何不使用ContextLoaderPlugIn也可以正常使用DelegatingRequestProcessor和DelegatingActionProxy整

         在整合Spring和Struts 1.x时候,网上面的教程都是教大家在struts配置里面增加一个叫org.springframework.web.struts.ContextLoaderPlugIn的插件,增加以下的配置:

struts-config.xml:

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


config.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN"
"http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans>
	<bean id="helloWorldService" class="com.strutstest.service.impl.HelloWorldServiceImpl">
	</bean>
	<!--注意此处的映射必须与Struts中的动作对应 -->
	<bean name="/helloWorld" class="com.strutstest.action.HelloWorldAction">
		<property name="helloWorldService">
			<ref bean="helloWorldService" />
		</property>
	</bean>
</beans>


         一般都是这样的方式的;这里ContextLoaderPlugin 的作用是通过contextConfigLocation配置的位置加载Spring的配置文件,生成WebApplicationContext对象,并将该对象放入ServletContext中,
对应的Key为:
ContextLoaderPlugIn.SERVLET_CONTEXT_PREFIX + "配置文件前缀"
//或者单纯的
ContextLoaderPlugIn.SERVLET_CONTEXT_PREFIX

        因此在struts运行时候就已经生成了对应的Spring环境了。Action可以被注册成Spring Bean,纳入Spring管理。

        但是有些时候,可以直接使用DelegatingRequestProcessor和DelegatingActionProxy而不需要使用ContextLoaderPlugIn来实现Action的Bean化。其实是因为我们已经使用了ContextLoaderListener/ContextLoaderServlet初始化了Spring的环境,使用时候我们只需要将Action的Bean加入到对应的Spring配置文件即可。

       对于 ContextLoaderPlugInContextLoaderListener/ContextLoaderServlet的区别主要在于两种方式生成的WebApplicationContext,以不同的Key存放在ServletContext中,ContextLoaderPlugIn是以
ContextLoaderPlugIn.SERVLET_CONTEXT_PREFIX + "配置文件前缀"
//或者单纯的
ContextLoaderPlugIn.SERVLET_CONTEXT_PREFIX

而ContextLoaderListener/ContextLoaderServlet则是
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE


        既然两个Key不同,那么 DelegatingRequestProcessorDelegatingActionProxy能识别到吗!

        DelegatingRequestProcessor是Spring提供的一个继承了struts的RequestProcessor类,使用时候直接在struts-config加入下面代码即可:
<!--告诉Struts用DelegatingRequestProcessor来代替原来的RequestProcessor -->
<controller processorClass="org.springframework.web.struts.DelegatingRequestProcessor" />


        由于DelegatingRequestProcessor继承于RequestProcessor,RequestProcessor是struts的核心控制器,通过其来控制Action的创建以及调用,Spring就是在这里做了手脚,Spring重写了RequestProcessor的processActionCreate方法,修改了创建Action方式,通过action 配置中path作为Bean Name,通过Spring容器获取正真的Action对象,然后执行对应的响应方法。

DelegatingRequestProcessor:
   private WebApplicationContext webApplicationContext;
   public void init(ActionServlet actionServlet, ModuleConfig moduleConfig) throws ServletException{
   super.init(actionServlet, moduleConfig);
   if (actionServlet != null) {
      this.webApplicationContext = initWebApplicationContext(actionServlet, moduleConfig);
      }
   }

   protected WebApplicationContext initWebApplicationContext(ActionServlet actionServlet, ModuleConfig moduleConfig) throws IllegalStateException {
      return DelegatingActionUtils.findRequiredWebApplicationContext(actionServlet, moduleConfig);
   }

   protected final WebApplicationContext getWebApplicationContext() {
      return this.webApplicationContext;
   }

   protected Action processActionCreate(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws IOException {
      Action action = getDelegateAction(mapping);
      if (action != null) {
         return action;
      }
      return super.processActionCreate(request, response, mapping);
   }

   protected Action getDelegateAction(ActionMapping mapping) throws BeansException {
      String beanName = determineActionBeanName(mapping);
      if (!getWebApplicationContext().containsBean(beanName)) {
         return null;
      }
      return (Action) getWebApplicationContext().getBean(beanName, Action.class);
   }

   protected String determineActionBeanName(ActionMapping mapping) {
      return DelegatingActionUtils.determineActionBeanName(mapping);
   }

DelegatingActionUtils:
public static WebApplicationContext findRequiredWebApplicationContext(ActionServlet actionServlet, ModuleConfig moduleConfig) throws IllegalStateException {
   WebApplicationContext wac = getWebApplicationContext(actionServlet, moduleConfig);
   // If no Struts-specific context found, fall back to root context.
   if (wac == null) {
      wac = WebApplicationContextUtils.getRequiredWebApplicationContext(actionServlet.getServletContext());
   }
   return wac;
}

public static String determineActionBeanName(ActionMapping mapping) {
   String prefix = mapping.getModuleConfig().getPrefix();
   String path = mapping.getPath();
   String beanName = prefix + path;
   if (logger.isDebugEnabled()) {
      logger.debug("DelegatingActionProxy with mapping path '" + path + "' and module prefix '" + prefix + "' delegating to Spring bean with name [" + beanName + "]");
   }
   return beanName;
}

public static WebApplicationContext getWebApplicationContext(ActionServlet actionServlet, ModuleConfig moduleConfig) {
   WebApplicationContext wac = null;
   String modulePrefix = null;
   // Try module-specific attribute.
   if (moduleConfig != null) {
      modulePrefix = moduleConfig.getPrefix();
      wac = (WebApplicationContext) actionServlet.getServletContext().getAttribute(ContextLoaderPlugIn.SERVLET_CONTEXT_PREFIX + modulePrefix);
   }
   // If not found, try attribute for default module.
   if (wac == null && !"".equals(modulePrefix)) {
      wac = (WebApplicationContext) actionServlet.getServletContext().getAttribute(ContextLoaderPlugIn.SERVLET_CONTEXT_PREFIX);
   }
   return wac;
}

WebApplicationContextUtils:
public static WebApplicationContext getWebApplicationContext(ServletContext sc) {
   Assert.notNull(sc, "ServletContext must not be null");
   Object attr = sc.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
   if (attr == null) {
      return null;
   }
   if (attr instanceof RuntimeException) {
      throw (RuntimeException) attr;
   }
   if (attr instanceof Error) {
      throw (Error) attr;
   }
   if (!(attr instanceof WebApplicationContext)) {
      throw new IllegalStateException("Root context attribute is not of type WebApplicationContext: " + attr);
   }
   return (WebApplicationContext) attr;
}

public static WebApplicationContext getRequiredWebApplicationContext(ServletContext sc) throws IllegalStateException {
   WebApplicationContext wac = getWebApplicationContext(sc);
   if (wac == null) {
      throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
   }
   return wac;
}


        很明显在 DelegatingRequestProcessorinit时候 WebApplicationContext就已经通过 DelegatingActionUtils.findRequiredWebApplicationContext方法获取到,而且很明显 findRequiredWebApplicationContext是先根据
ContextLoaderPlugIn.SERVLET_CONTEXT_PREFIX + "配置文件前缀"

获取,如果没有就根据
ContextLoaderPlugIn.SERVLET_CONTEXT_PREFIX

获取,如果再没有获取到,就根据
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE

获取,如果还是没有获取到就直接抛出异常。

        DelegatingRequestProcessor是会在 ServletContext查找 ContextLoaderPlugInContextLoaderListener/ContextLoaderServlet的Key,获取 WebApplicationContext,然后通过 WebApplicationContext获取对应的Action。因此 DelegatingRequestProcessor是可以在没有 ContextLoaderPlugIn的情况下正常运行的。

        对于DelegatingActionProxy也是差不多的原理。

DelegatingActionProxy:
public ActionForward execute(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) throws Exception {
   Action delegateAction = getDelegateAction(mapping);
   return delegateAction.execute(mapping, form, request, response);
}

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

protected WebApplicationContext getWebApplicationContext(ActionServlet actionServlet, ModuleConfig moduleConfig) throws IllegalStateException {
   return DelegatingActionUtils.findRequiredWebApplicationContext(actionServlet, moduleConfig);
}

protected String determineActionBeanName(ActionMapping mapping) {
   return DelegatingActionUtils.determineActionBeanName(mapping);
}


        DelegatingActionProxyexecute时候也是通过 DelegatingActionUtils.findRequiredWebApplicationContext获取 WebApplicationContext,然后获取Action,来达到目的的。

附加:
        配置使用Spring的 OpenSessionInView Filter时候,因为 OpenSessionInViewFilter是按照
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE

这个key去拿spring配置的!

        整理一下思路:
ContextLoaderPlugIn保存spring配置的key:
ContextLoaderPlugIn.SERVLET_CONTEXT_PREFIX + "配置文件前缀"
//或者单纯的
ContextLoaderPlugIn.SERVLET_CONTEXT_PREFIX

ContextLoaderListener保存spring配置的key
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE

OpenSessionInView是按照
WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE

这个名字去取得spring容器的!而你的应用程序却是按照 ContextLoaderPlugIn的key去取得spring容器的。所以, OpenSessionInView模式失效!
        如果使用了 ContextLoaderPlugIn的Spring容器,而 OpenSessionInViewFilter却获取了 ContextLoaderListener的Spring容器。这样会导致Hibernate的session异常关闭的问题。如果配置了两个Spring容器,则刪除 ContextLoaderPlugIn的加载方式。若只配置了 ContextLoaderPlugIn的加载方式,则改成 ContextLoaderListener加载方式。

你可能感兴趣的:(java,spring,Web,bean,struts)