這篇文章主要介紹了struts1之ActionServlet詳解,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
在web.xml中我們除了配置ActionServlet還配置了一些初始化參數信息,首先我們看第一個config參數,這裡配置的是/WEB-INF/struts -config.xml,因為要下面傳遞一個這樣一個配置訊息,這個xml檔名是struts1標準的名字,所以這裡這個初始化訊息完全可以刪除,如果不用這個標準名稱這裡就必須要在這裡配置。現在我們配置的是標準名字,所以我們可以刪除,這是為什麼呢?這裡要看ActionServlet原始碼才可以。
從圖片上我們可以看到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存入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完成。
以上是struts1之ActionServlet程式碼分享的詳細內容。更多資訊請關注PHP中文網其他相關文章!