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

网友投稿 210 2023-04-09


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

在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方法的源代码如下:

/**

*

Initialize this servlet. Most of the processing has been factored into

* support methods so that you can overrideparticular functionality at a

* fairly granular level.

*

* @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()方法。这个方法的实现代码是:

代码段一:

/**

*

Initialize our internal MessageResourcesbundle

*

* @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 MessageResources for the

* created by the default MessageResourcesFactory.

*

* @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 MessageResourcesFactory instance ofthe

* appropriate class, which can be used tocreate customized

* MessageResourcesinstances If no such factory can be

* created, return null 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,另外这个方法还会注册一些转换类的。具体源代码是:

/**

*

Initialize other global characteristics ofthe controller servlet

*

* @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);

ConvertUtihttp://ls.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中。具体实现源代码:

/**

*

Initialize the servlet mapping under which our controller servlet

* is being accessed. This will be used in the &html:form>

* tag to generate correct destination URLs for form submissions.

*

* @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);

dighttp://ester.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].getDynaActionFormClaskiLrXes();

}

}

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完成。


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:使用import导入静态成员的方法 原创
下一篇:React教程之Props验证的具体用法(Props Validation)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~