欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

struts1之ActionServlet详解_动力节点Java学院整理

程序员文章站 2024-02-24 18:50:34
在web.xml中我们除了配置actionservlet还配置了一些初始化参数信息,首先我们看第一个config参数,这里配置的是/web-inf/struts-confi...

在web.xml中我们除了配置actionservlet还配置了一些初始化参数信息,首先我们看第一个config参数,这里配置的是/web-inf/struts-config.xml,因为要下面传递一个这样一个配置信息,这个xml文件名是struts1标准的名字,所以这里这个初始化信息完全可以删除,如果不用这个标准名称这里就必须要在这里配置。现在我们配置的是标准名字,所以我们可以删除,这是为什么呢?这里要看actionservlet源代码才可以。

struts1之ActionServlet详解_动力节点Java学院整理

从图片上我们能看到actionservlet中已经写好了默认的config信息了,就是标准名字。所以这里删除也是可以的。
在看下面的debug和detail参数,这两个参数信息是有关日志信息级别的设置,主要关于解析配置文件/web-inf/struts-config.xml级别的初始化参数。这里这两个参数可以完全去掉也不影响。

最后还有一个load-on-startup配置,这个是初始化servlet级别的初始化信息,这个参数如果大于等于0就是说明在服务器一启动就把servlet初始化,也就是调用actionservlet的init方法,这个也可以到actionservlet的源代码中去查找。

当actionservlet初始化的时候就会读取/web-inf/struts-config.xml信息到内存中,读到内存是以什么样的形式展现的呢?我们现在可以看一下以前博客的那个mvc实例,那里面读取配置文件中的信息是以actionmapping的形式展现的。另外servlet-mapping的配置就不讲解了,这个都知道就是匹配url路径的,当遇到url-pattern的路径时候就会实例化actionservlet。

通过这篇文章我们知道了当我们请求的时候actionservlet是怎样实例化的,也知道为什么我们要配置web.xml信息了。那么我们为什么要配置/web-inf/struts-config.xml文件,actionservlet是如何传递请求的,如何和actionform、actionmapping、action等交互的最终完成用户请求的呢?

我们先从actionservlet源代码的init方法开始。因为actionservlet就是一个servlet,它也是具有典型的那几个方法init、doget、dopost等方法。既然是初始化,那么我们就要看init方法。init方法的源代码如下:

/** 
   * <p>initialize this servlet. most of the processing has been factored into 
   * support methods so that you can overrideparticular functionality at a 
   * fairly granular level.</p> 
   * 
   * @exception servletexception if we cannotconfigure ourselves correctly 
   */ 
  publicvoidinit() throwsservletexception { 
  
    // wraps the entire initialization in a try/catch tobetter handle 
    // unexpected exceptions and errors to provide better feedback 
    // to the developer 
    try { 
      initinternal(); 
      initother(); 
      initservlet(); 
   
      getservletcontext().setattribute(globals.action_servlet_key, this); 
      initmoduleconfigfactory(); 
      // initialize modules as needed 
      moduleconfig moduleconfig =initmoduleconfig("", config); 
      initmodulemessageresources(moduleconfig); 
      initmoduledatasources(moduleconfig); 
      initmoduleplugins(moduleconfig); 
      moduleconfig.freeze(); 
   
      enumeration names =getservletconfig().getinitparameternames(); 
      while (names.hasmoreelements()) { 
        string name = (string)namesnextelement(); 
        if (!name.startswith("config/")) { 
          continue; 
        } 
        string prefix =name.substring(6); 
        moduleconfig = initmoduleconfig 
          (prefix,getservletconfig().getinitparameter(name)); 
        initmodulemessageresources(moduleconfig); 
        initmoduledatasources(moduleconfig); 
        initmoduleplugins(moduleconfig); 
        moduleconfig.freeze(); 
      } 
   
      this.initmoduleprefixes(this.getservletcontext()); 
   
      thisdestroyconfigdigester(); 
    } catch (unavailableexception ex) { 
      throw ex; 
    } catch (throwable t) { 
  
      // the follow error message is not retrieved from internal message 
      // resources as they may not have been able to have been 
      // initialized 
      logerror("unable to initialize struts actionservlet due to an " 
        + "unexpected exception or error thrown, so marking the " 
        + "servlet as unavailable. mostlikely, this is due to an " 
        + "incorrect or missing library dependency.", t); 
      throw new unavailableexception(t.getmessage()); 
    }   
} 

在解释这段代码的流程和意思之前,有必要说一句,就是当我们在eclipse里面看代码的时候,尤其是看一段生疏的很长的代码的时候,希望能够经常使用ctrl键(多余的不解释)。

下面开始讲解这段代码的流程和具体每一步的含义,如果有不正确的地方,希望指正。

首先映入眼帘的是initinternal()方法。这个方法的实现代码是:

代码段一:

/** 
   * <p>initialize our internal messageresourcesbundle</p> 
   * 
   * @exception servletexception if we cannotinitialize these resources 
   */ 
  protectedvoidinitinternal() throwsservletexception { 
  
    // :fixme: document unavailableexception 
  
    try { 
      internal = messageresourcesgetmessageresources(internalname); 
    } catch (missingresourceexception e) { 
      log.error("cannot load internal resources from '"+ internalname+ "'", 
        e); 
      throw new unavailableexception 
        ("cannot load internal resources from '"+ internalname+ "'"); 
    } 
  
} 

代码段二:

/** 
   * create and return an instance of <code>messageresources</code> for the 
   * created by the default <code>messageresourcesfactory</code>. 
   * 
   * @param config configuration parameterfor this message bundle. 
   */ 
  publicsynchronizedstaticmessageresources getmessageresources(string config) { 
  
    if (defaultfactory == null) { 
      defaultfactory =messageresourcesfactory.createfactory(); 
    } 
  
    return defaultfactory.createresources(config); 
} 

代码段三:

/** 
   * create and return a <code>messageresourcesfactory</code> instance ofthe 
   * appropriate class, which can be used tocreate customized 
   * <code>messageresources</code>instances if no such factory can be 
   * created, return <code>null</code> instead 
   */ 
  publicstaticmessageresourcesfactory createfactory(){ 
  
    // construct a new instance of the specified factory class 
    try { 
      if (clazz == null) 
        clazz = requestutils.applicationclass(factoryclass); 
      messageresourcesfactory factory = 
        (messageresourcesfactory) clazz.newinstance(); 
      return (factory); 
    } catch (throwable t) { 
      log.error("messageresourcesfactory.createfactory",t); 
      return (null); 
    } 
  
} 

这个方法的具体作用就是初始化messageresources,具体实现是工厂模式,首先判断defaultfactory是否存在,不存在则创建工厂,defaultfactory = messageresourcesfactory.createfactory(),在通过工厂创建资源类defaultfactory.createresources(config);存在则直接创建资源类。

initother()的方法,主要是初始化其它的配置,获取我们自己的struts-config配置文件的路径,而它的默认路径就是web-inf/struts-config.xml,另外这个方法还会注册一些转换类的。具体源代码是:

/** 
   * <p>initialize other global characteristics ofthe controller servlet</p> 
   * 
   * @exception servletexception if we cannotinitialize these resources 
   */ 
  protectedvoidinitother() throwsservletexception { 
  
    string value = null; 
    value =getservletconfig().getinitparameter("config"); 
    if (value != null) { 
      config = value; 
    } 
  
    // backwards compatibility for form beans of java wrapper classes 
    // set to true for strict struts 0 compatibility 
    value =getservletconfig().getinitparameter("convertnull"); 
    if ("true".equalsignorecase(value) 
      || "yes".equalsignorecase(value) 
      || "on".equalsignorecase(value) 
      || "y".equalsignorecase(value) 
      || "1".equalsignorecase(value)) { 
  
      convertnull = true; 
    } 
  
    if (convertnull) { 
      convertutils.deregister(); 
      convertutils.register(new bigdecimalconverter(null), bigdecimal.class); 
      convertutils.register(new bigintegerconverter(null), biginteger.class); 
      convertutils.register(new booleanconverter(null), boolean.class); 
      convertutils.register(new byteconverter(null), byte.class); 
      convertutils.register(new characterconverter(null), character.class); 
      convertutils.register(new doubleconverter(null), double.class); 
      convertutils.register(new floatconverter(null), float.class); 
      convertutils.register(new integerconverter(null), integer.class); 
      convertutils.register(new longconverter(null), long.class); 
      convertutils.register(new shortconverter(null), short.class); 
    } 
  
} 

initservlet()方法是利用digester读取web.xml文件并且放到servletcontext中。具体实现源代码:

/** 
 * <p>initialize the servlet mapping under which our controller servlet 
 * is being accessed. this will be used in the <code>&html:form></code> 
 * tag to generate correct destination urls for form submissions.</p> 
 * 
 * @throws servletexception if error happens while scanning web.xml 
 */ 
protected void initservlet() throws servletexception { 
 
  // remember our servlet name 
  this.servletname = getservletconfig().getservletname(); 
 
  // prepare a digester to scan the web application deployment descriptor 
  digester digester = new digester(); 
  digester.push(this); 
  digester.setnamespaceaware(true); 
  digester.setvalidating(false); 
 
  // register our local copy of the dtds that we can find 
  for (int i = 0; i < registrations.length; i += 2) { 
    url url = this.getclass().getresource(registrations[i+1]); 
    if (url != null) { 
      digester.register(registrations[i], url.tostring()); 
    } 
  } 
 
  // configure the processing rules that we need 
  digester.addcallmethod("web-app/servlet-mapping", 
              "addservletmapping", 2); 
  digester.addcallparam("web-app/servlet-mapping/servlet-name", 0); 
  digester.addcallparam("web-app/servlet-mapping/url-pattern", 1); 
 
  // process the web application deployment descriptor 
  if (log.isdebugenabled()) { 
    log.debug("scanning web.xml for controller servlet mapping"); 
  } 
 
  inputstream input = 
    getservletcontext().getresourceasstream("/web-inf/web.xml"); 
 
  if (input == null) { 
    log.error(internal.getmessage("configwebxml")); 
    throw new servletexception(internal.getmessage("configwebxml")); 
  } 
 
  try { 
    digester.parse(input); 
 
  } catch (ioexception e) { 
    log.error(internal.getmessage("configwebxml"), e); 
    throw new servletexception(e); 
 
  } catch (saxexception e) { 
    log.error(internal.getmessage("configwebxml"), e); 
    throw new servletexception(e); 
 
  } finally { 
    try { 
      input.close(); 
    } catch (ioexception e) { 
      log.error(internal.getmessage("configwebxml"), e); 
      throw new servletexception(e); 
    } 
  } 
 
  // record a servlet context attribute (if appropriate) 
  if (log.isdebugenabled()) { 
    logdebug("mapping for servlet '" + servletname + "' = '" + 
      servletmapping + "'"); 
  } 
 
  if (servletmapping != null) { 
    getservletcontext().setattribute(globals.servlet_key, servletmapping); 
  } 
 
} 

首先说在说之前还是先讲init方法的具体实现代码写出来以便大家方便阅读和理解。

init源代码:

public void init() throws servletexception { 
  
 try { 
    //初始化资源类 
   initinternal(); 
   //注册转换类 
   initother(); 
   //利用digester读取webxml文件并且将其放到servletcontext中 
   initservlet(); 
   getservletcontext().setattribute(globals.action_servlet_key, this); 
    
   initmoduleconfigfactory(); 
   moduleconfig moduleconfig = initmoduleconfig("", config); 
   initmodulemessageresources(moduleconfig); 
   initmoduledatasources(moduleconfig); 
   initmoduleplugins(moduleconfig); 
   moduleconfig.freeze(); 
 
   enumeration names = getservletconfig().getinitparameternames(); 
   while (names.hasmoreelements()) { 
     string name = (string) names.nextelement(); 
     if (!name.startswith("config/")) { 
       continue; 
     } 
     string prefix = name.substring(6); 
     moduleconfig = initmoduleconfig 
       (prefix, getservletconfig()getinitparameter(name)); 
     initmodulemessageresources(moduleconfig); 
     initmoduledatasources(moduleconfig); 
     initmoduleplugins(moduleconfig); 
     moduleconfig.freeze(); 
   } 
 
   this.initmoduleprefixes(this.getservletcontext()); 
 
   this.destroyconfigdigester(); 
 } catch (unavailableexception ex) { 
   throw ex; 
 } catch (throwable t) { 
   log.error("unable to initialize struts actionservlet due to an " 
     + "unexpected exception or error thrown, so marking the " 
     + "servlet as unavailable most likely, this is due to an " 
     + "incorrect or missing library dependency", t); 
   throw new unavailableexception(t.getmessage()); 
 }   
} 

getservletcontext().setattribute(globals.action_servlet_key,this);这句话是将actionservlet实例将以globals.action_servlet_key作为key存入servletcontext中。

 这里的globals.action_servlet_key在actionservlet已经给出了声明:

public static final string action_servlet_key= "org.apache.struts.action.action_servlet"; 

接下来initmoduleconfigfactory()方法,这个方法主要的作用是解析在web.xml中configfactory的text值。如果configfactory有配置,则将设置moduleconfigfactory中得factoryclass值,否则默认得为efaultmoduleconfigfactory。该方法其实宗旨是让开发人员自己开发出moduleconfigfactory,从而得到自己所需要的moduleconfig类。因为我们的实例中没有配置这个参数信息,所以我们这里的实例是要defalutmodelconfigfactory了。

代码段一:

protected voidinitmoduleconfigfactory(){ 
    string configfactory =getservletconfig().getinitparameter("configfactory"); 
    if (configfactory != null) { 
      moduleconfigfactory.setfactoryclass(configfactory); 
    } 
} 

代码段二:

public static void setfactoryclass(string factoryclass) { 
    moduleconfigfactory.factoryclass = factoryclass; 
    moduleconfigfactory.clazz = null; 
  } 

代码段三:

protected static string factoryclass = 
    "org.apache.struts.config.impl.defaultmoduleconfigfactory"; 
} 

moduleconfig moduleconfig =initmoduleconfig("", config)方法是非常重要的,initmoduleconfig方法给strits-config里面的属性初始化后放入moduleconfig对象里面去,放到moduleconfig对象里面去便于以后操作更快,因为它是文件流。

具体实现代码:

protected moduleconfig initmoduleconfig(stringprefix, string paths) 
    throws servletexception { 
  
    // :fixme: document unavailableexception? (doesn't actually throw anything) 
  
    if (log.isdebugenabled()) { 
      log.debug( 
        "initializing module path '" 
          + prefix 
          + "' configuration from '" 
          + paths 
          + "'"); 
    } 
  
    // parse the configuration for this module 
    moduleconfigfactory factoryobject= moduleconfigfactory.createfactory(); 
    moduleconfig config =factoryobject.createmoduleconfig(prefix); 
  
    // configure the digester instance we will use 
    digester digester =initconfigdigester(); 
  
    // process each specified resource path 
    while (paths.length() > 0) { 
      digester.push(config); 
      string path = null; 
      int comma = paths.indexof(','); 
      if (comma >= 0) { 
        path =paths.substring(0, comma).trim(); 
        paths =paths.substring(comma + 1); 
      } else { 
        path = pathstrim(); 
        paths = ""; 
      } 
  
      if (pathlength() < 1){ 
        break; 
      } 
  
      this.parsemoduleconfigfile(digester,path); 
    } 
  
    getservletcontext().setattribute( 
      globals.module_key +config.getprefix(), 
      config); 
  
  
    // force creation and registration of dynaactionformclass instances 
    // for all dynamic form beans we wil be using 
    formbeanconfig fbs[] =config.findformbeanconfigs(); 
    for (int i = 0; i < fbs.length; i++) { 
      if (fbs[i].getdynamic()) { 
        fbs[i].getdynaactionformclass(); 
      } 
    } 
  
    return config; 
} 

这里有必要解析一下这段代码。首先得到继承moduleconfigfactory的实现类,如果在initmoduleconfigfactory()中能设置factoryclass属性,则能生成客户化得factory,否则得到得是默认得defaultmoduleconfigfactory类,该工厂得到moduleconfigimpl类。然后调用initconfigdigester()该方法为解析配置文件做准备,初始化digest类(具体digest的初始化实现就不讲解)。最后返回moduleconfig,而这时的moduleconfig里面封装了所有的struts-config.xml中的信息。

最后的几个方法就简单说一下就行,不是非常难理解:

initmodulemessageresources(moduleconfig)方法是通过moduleconfig中的配置文件信息,创建messageresource对象.

initmoduledatasources(moduleconfig)方法是通过moduleconfig中的配置文件信息,创建datasource对象.   initmoduleplugins(moduleconfig)加载并初始化默认应用模块的所有插件的。

moduleconfig.freeze()是将配置文件中的各个对象,设置成已配置状态.

最后我们看到了,下面还有一段同上面代码的循环代码,这段代码的主要意思就是当默认子应用模块被成功初始化后,如果应用还包括其他子应用模块,将重复流程,分别对其他子应用模块进行初始化。这个也是很好理解的。

到此为止actionservlet就init完成。