第一篇 struts的初始化
struts 的核心类是org.apache.struts.action.ActionServlet,这个类将会在struts第一次使用时,
作为 servlet初始化并存入tomcat容器。很显然的,初始化将会调用init方法初始化相应的数据。
一、initInternal()方法:
通过调用 MessageResources.getMessageResources(internalName)方法生成一个
MessageResources 类,getMessageResources是通过调用MessageResourcesFactory.
createResources(config) 来实现的。至于MessageResourcesFactory是一个abstract类,任何
继承自它的类都要实现 createResources方法,生成MessageResources对象。整个程序生成
MessageResourcesFactory 使用了如下技巧:
MessageResourcesFactory.factoryClass = factoryClass;
MessageResourcesFactory.clazz = null;
首先会通过factoryClass来定义一个类全名,然后通过ClassLoader.loadClass
(factoryClass) 方法来生成这个类,并赋给clazz,然后通过newInstance来生成一个对象。
在本程序中,生成MessageResources对象实 际就是对如下属性进行了初始化:
this.factory = factory;("org.apache.struts.util.PropertyMessageResourcesFactory")
this.config = config;("org.apache.struts.action.ActionResources")
this.returnNull = returnNull;(true/false)
对于MessageResources类的作用是根据不同的Locate来格式化相应的string。或者把你需要改变
的string存放 到数组中,然后通过getMessage(Locale locale, String key, Object args[])
方法来格式化。 然后把格式好的string存放到HashMap里,这样就可以为以后重用。这里的key是
使用的locale.toString() + "." + key
在PropertyMessageResources中的loadLocale方法用来读取resource的初始化信息。首先它会
通过一 个HashMap检测这个localKey相关的message是否已经被初始化了,如果被初始化过就跳
出,检测的方法是 locales.get(localeKey) != null。
然后会读取如下一个文件:
org/apache/struts /action/ActionResources_(localKey).properties,然后进行如下操作:
Properties props = new Properties();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
is = classLoader.getResourceAsStream(name);
props.load(is);
Iterator names = props.keySet().iterator();
while (names.hasNext()) {
String key = (String) names.next();
if (log.isTraceEnabled()) {
log.trace(" Saving message key '" + messageKey(localeKey, key));
}
messages.put(messageKey(localeKey, key), props.getProperty(key));
}
PropertyMessageResources 就是通过上面的loadLocale方法查找与Locale locale, String key
相对对应的Message.查找的次序如下locale.toString(),然后是
localeKey = localeKey.substring(0, underscore),然后是defaultLocale,然后是key。
最后,resource类的结构如下:
PropertyMessageResources extends MessageResources
PropertyMessageResourcesFactory extends MessageResourcesFactory
二、initOther()方法:
从servlet中获取config和debug两个参数,然后初始化ConvertUtils对象。由 于
ConvertUtils.deregister()的初始化,所有的Converter都是有初始值的,所以这里Struts自己
把 这些初始值设置为null,即转换出错的时候返回null,而不是初始值。使用ConvertUtils类的
原因是由于从form传输过来的都是 String类型的值,所以我们要把它们转换成相应的类型。
提到几个技巧:
*public boolean isIndexed() {
if (type == null) {
return (false);
//技巧一:判断是否是一个Array类的方法
} else if (type.isArray()) {
return (true);
//技巧二:判断type是否是List的一个父类或者父接口,或者与List为同一个类
//要注意如果List是另一 个primitive的TYPE类,那么type必须也是这个类才会
//返回true,否则都是false。注意long.TYPE与 Long.class是不同的
} else if (List.class.isAssignableFrom(type)) {
return (true);
} else {
return (false);
}
}
*//componentType为Array类所存储的元素的类别
Class componentType = indexedProperty.getClass().getComponentType();
//生成一个新的Array
Object newArray = Array.newInstance(componentType, (index + 1));
System.arraycopy(indexedProperty, 0, newArray, 0, length);
indexedProperty = newArray;
set(name, indexedProperty);
int newLength = Array.getLength(indexedProperty);
for (int i = length; i < newLength; i++) {
Array.set(indexedProperty, i, createProperty(name+"["+i+"]", componentType));
}
三、initServlet()方法:
这个方法主要是通过digester类解析web.xml,对String servletMapping属性进行初始化。对于
digester说明如下:这是一个基于DOM的SAX实现的类,它是事件触发的,根据xml 文件的结构,
每次读到一个节点元素就会触发一个事件。
InputStream input = getServletContext().getResourceAsStream("/WEB-INF/web.xml");
这是一个比较少见 的方法。首先通过this.servletName = getServletConfig().
getServletName()获取 servlet的名称,然后根据
if (servletName.equals(this.servletName)) {
this.servletMapping = urlPattern;
}
来判断当前读到的servlet名称是否是我们运行的servlet的名称,如果是,就把url- pattern作为
我们的servletMapping。
四、getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this)
把 自己存储到servletContext中,属性名为Globals.ACTION_SERVLET_KEY。
五、ModuleConfig moduleConfig = initModuleConfig("", config)
这个方法使用由 initOther()方法获取的config值为要解析的xml路径,用来初始化ModuleConfig。
它首先采用与生成 MessageResourcesFactory同样的方法产生一个MessageResourcesFactory对象:
MessageResourcesFactory 为一个抽象类,每一个继承它的类都要实现
createModuleConfig(String prefix)方法。本程序使用的缺省的MessageResourcesFactory类为
org.apache.struts.config.impl.DefaultModuleConfigFactory, 它
的createModuleConfig(String prefix)方法会生成一个ModuleConfigImpl类。
ModuleConfigImpl类相当于一个JavaBean,用来存放一个web模块运行时所需要的配置信息。当
然,一个web模块可 以拥有多个ModuleConfig,但是缺省的是prefix长度为0的ModuleConifg。它
的每个属性几乎都是由HashMap组成 的,它通过一个configured布尔值来描述当前的ModuleConfig
是否已经被初始化完毕,在每存放一个属性的时候都会监测这个值。 如果初始化完毕而还要改变
里面的属性值,则会报出IllegalStateException("Configuration is frozen")异常,现在对它
的属性简单说明如下:
* protected HashMap actionConfigs:
这 个HashMap用来存储ActionConfig对象。
* protected HashMap dataSources
这个 HashMap用来存储DataSourceConfig对象。
* protected HashMap exceptions
这个 HashMap用来存储ExceptionConfig对象。
* protected HashMap formBeans
这个 HashMap用来存储FormBeanConfig对象。
* protected HashMap forwards
这个 HashMap用来存储ForwardConfig对象。
* protected HashMap messageResources
这 个HashMap用来存储MessageResourcesConfig对象。
* protected ArrayList plugIns
这 个HashMap用来存储PlugInConfig对象。
* protected ControllerConfig controllerConfig
ControllerConfig类
* protected boolean configured
标 志这个ModuleConfig是(true)否(false)配置完成。
* protected String prefix
用来标志 和区分ModuleConfig类,同时在使用上面的config类初始化相应的资源以后,也是通
过这个prefix来区分所属的不同的web模 块。
* protected String actionMappingClass = "org.apache.struts.action.ActionMapping"
ActionMapping类名,缺省为 org.apache.struts.action.ActionMapping。
初始化ModuleConfig的方法如下:
首先是使用 getServletConfig().getInitParameter("mapping")来获取设定的ActionMapping类
名, 然后通过initConfigDigester()方法来生成一个digester。最后用","分隔config,对每一
块调用 parseModuleConfigFile(prefix, paths, config, digester, path)方法解析。注意,这
个 方法实际上只有两个参数是有意义的:path为我们要解析的xml文件,config用来初始化完成
后保存到servletContext中。
如果ModuleConfig中存放的FormBeanConfig为Dydamic类型,那么就调用
DynaActionFormClass.createDynaActionFormClass(FormBeanConfig) 初始化
DynaActionFormClass,并存放到DynaActionFormClass.dynaClasses 的 static HashMap中。这
里的key为FormBeanConfig.getName() + moduleConfig.getPrefix()。
如果当前的ModuleConfig为缺省的ModuleConfig,那么将会调用如下几个方法:
defaultControllerConfig(config)
defaultMessageResourcesConfig(config)
defaultFormBeansConfig(config)
defaultForwardsConfig(config)
defaultMappingsConfig(config)
在 struts1.1以后,这个特例将会被废弃:
defaultControllerConfig(config)为ControllerConfig通过getInitParameter(s) 方法初始化如
下几个属 性:bufferSize,content,locale(true/false),maxFileSize,nocache(true/false)
,multipartClass,tempDir。
defaultMessageResourcesConfig(config)为MessageResourcesConfig通过 getInitParameter(s)
方法初始化如下几个属性:application,factory,null(true/false)。
其它的几个方法就是获取不同的对象,然后把它们相应的存储到servlet中。关心如下:
ActionFormBeans=> FormBeanConfig,ActionForwards=>ForwardConfig,
ActionMappings=> ActionConfig。
六、initModuleMessageResources(ModuleConfig config)
通过存储在 ModuleConfig中的MessageResourcesConfig对象,逐个初始化MessageResource,
然后再把初始化好 的MessageResources存放到ServletContext中,attributeName为
MessageResourcesConfig.getKey() + ModuleConfig.getPrefix()。
七、initModuleDataSources(ModuleConfig config)
通过存储在ModuleConfig中的 DataSourceConfig对象,逐个初始化DataSource。然后对于每一个
DateSource通过 BeanUtils.populate(ds, dscs[i].getProperties())方法初始化其属性。再把初
始化好的 DateSource存放到ServletContext中,attributeName为
DataSourceConfig.getKey() + ModuleConfig.getPrefix()。同时也存放到名位dataSources的
FastHashMap中,key为 DataSourceConfig.getKey()。
这里还会根据生成的DateSource对象是否是GenericDataSource类型,如果是则调用
GenericDataSource.open() 方法。GenericDataSource是一个非常简单的数据库连接池,它的
open()方法用来初始化连接池,生成最小数目的 GenericConnection,这里的open()方法根据
String driver变量是否为null来判断是否已经被初始化过。需要仔细说明的是getConnection()
方法,它首先从连接池中取出 GenericConnection对象,然后检查其是否是可链接的,如果是就
返回,否则继续取出,同时activeCount-1。如果没有取 到,则会检查当前可使用的
GenericConnection是否达到最大值(activeCount < maxCount),如果没有,调用
createConnection()方法声成一个新的GenericConnection,然后检查其是否 是可链接,如果可以
则返回。returnConnection(GenericConnection conn)方法则是通过把GenericConnection放回到
连接池,然后activeCount-1。
这个方法中使用到了ServletContextWriter类,DateSource的log信息就通过这个类写入。对这个
类说明如下:
它 继承自PrintWriter,而PrintWriter又继承自Writer。Writer类所作的事情就是在同步的情况下
调用 abstract方法:abstract public void write(char cbuf[], int off, int len),这个方法
将会根据调用者的需要由调用者实现。
PrintWriter则首先通过ensureOpen()方法检验这个类中是否 有写入的对象(Writer类或其子类),
如果有则根据不同的情况调用这个写入对象的write方法(out.write(....))。这个类 的print(...)
方法就是据不同的情况调用相应的write(...)方法。而println(...)与之的区别就是每次多写入一
个 换行字符串。还有一个区别是println(...)会根据是否需要autoflush进行flush,而write(...)
方法不会。
ServletContextWriter 类的作用是把字符写入ServletContext中。ServletContextWriter类方法中
真正实现了write方法:
public void write(char c) {
if (c == '\n')
flush();
else if (c != '\r')
buffer.append(c);
}
public void flush() {
if (buffer.length() > 0) {
context.log(buffer.toString());
buffer.setLength(0);
}
}
八、initModulePlugIns(moduleConfig)
通过存储在ModuleConfig中的PlugInConfig对 象,逐个初始化PlugIn对象,存放到一个数组中,
然后再把这个数组存放到ServletContext中,attributeName为
Globals.PLUG_INS_KEY + ModuleConfig.getPrefix()。
对每一个生成的PlugIn对象通过
BeanUtils.populate(plugIns[i], plugInConfigs[i].getProperties())方法初始化其属性。然后
再把PlugInConfig对象存放到由其生成的 PlugIn对象中。
最后,通过plugIns[i].init(this, (ModuleConfig) config)初始化这个plugIn对象。
九、初始化结束
完成了这个初始化以后,会调用ModuleConfig.freeze()令这个ModuleConfig变得不可改变。然 后
会遍历ServletConfig中的initParameterNames,如果有以"config/"开头的,则通过这个 parameter
的值继续初始化其它的ModuleConfig,且这个ModuleConfig的prefix为"config/"后的字符 串。
同样调用如下方法:
initModuleMessageResources(moduleConfig);
initModuleDataSources(moduleConfig);
initModulePlugIns(moduleConfig);
moduleConfig.freeze();
最后调用destroyConfigDigester()释放内存。
第二篇 struts的执行
本篇详细介绍struts在初始化之后是如何处理一个请求,并返回数据的。这里最核心 的类是
RequestProcessor以及RequestUtils。RequestProcessor类通过 RequestDispatcher实现页面的跳转,
而RequestProcessor负责处理request中传来的请求信息,存放到 FormBeanConfig中,以及对要跳转的
url进行处理。
struts 在初始化完成之后,会根据请求调用doGet(...)或者doPost(...)方法,这两个方法直接
调用 process(request, response)方法。process(...)方法首先判断当前的request属于
哪一个 ModuleConfig,然后生成与这个ModuleConifg相对应的RequestProcessor,最后调用这个
RequestProcessor 的process(...)方法,执行request的请求。
一、RequestUtils.selectModule(String prefix, HttpServletRequest,ServletContext)方法:
这个方法,根据prefix,从ServletContext中选 择相应的ModuleConfig,然后把这个ModuleConfig
保存到request中。ServletContext对应的key值为 Globals.MODULE_KEY + prefix,保存到request
中使用的key值为Globals.MODULE_KEY。如果 在ServletContext中不存在这样的一个ModuleConfig,
那么调用 request.removeAttribute(Globals.MODULE_KEY)方法。然后以同样的方法查找、保存
MessageResources 对象。当prefix为空时,会调用下面的方法选择ModuleConfig。
二、RequestUtils.selectModule(HttpServletRequest, ServletContext)
这个 方发首先使用getModuleName(HttpServletRequest, ServletContext)获取相应的path,然后
通 过调用getModuleName(String matchPath, ServletContext)获取相应的prefix。以这prefix为
参 数调用(一)中的selectModule(...)选择ModuleConfig。
获取path的过程为:首先从request中查找名称为
INCLUDE_SERVLET_PATH(javax.servlet.include.servlet_path) 的属性,如果为空,就调用
request.getServletPath()方法获取servletPath。
获取prefix的过程为:它首先调用getModulePrefixes(ServletContext),获取所有已存在的
module 前缀。 然后通过分析上面获取的path来判断当前的url属于哪一个module,方法是截取
path中最后一个"/"字符前面的字符串,然后 与有上面方法中获取的prefixes[]逐个对比,如果
prefixes[]中存在这样的一个值,则返回这个截取的字符串,否则继续截取 path最后面的"/"前面
的字符串,然后对比。如果始终找不到,则返回""。
getModulePrefixes(ServletContext)的执行过程是:首先通过
context.getAttribute(PREFIXES_KEY) 查找是否存在这样的一个保存所有的prefix的string array,
如果存在,就说明已经解析过一次了,就直接返回这个string array,否则遍历ServletContext中
所有的attribute name,查找以MODULE_KEY(org.apache.struts.action.MODULE)开头的
atrribute name,然后截取这个atrribute name中MODULE_KEY后面的字符串,作为一个prefix. 最
后把通过这个这个方式获取 的所有的prefix作为一个ArrayList存储到ServletContext中,属性key
值为 PREFIXES_KEY(org.apache.struts.util.PREFIXES),这样下次再次查找的时候就可以直接从这
个 attribute中获取了。
三、getModuleConfig(HttpServletRequest)
这个方法就是获取上面的selectModule(...) 方法所得到的ModuleConfig。如果找不到这样的
ModuleConfig,那么就把ServletContext中缺省的 ModuleConfig返回(调用
getServletContext().getAttribute(Globals.MODULE_KEY))
四、getRequestProcessor(ModuleConfig config)
这个方法从根据ModuleConfig的 prefix作为key,从ServletContext中获取RequestProcessor。这
个key值为 Globals.REQUEST_PROCESSOR_KEY + config.getPrefix()。
如果ServletContext中不存在这样的一个RequestProcessor,那么就生成一个新的
RequestProcessor 的实例,完成初始化(保存ActionServlet以及ModuleConfig到这个新的实例中)后
将其保存到ServletContext 中。
五、RequestProcessor.process(HttpServletRequest, HttpServletResponse)
这 是真正执行HttpServletRequst请求的方法。
这个方法首先判断但前的HttpServletRequest是否是Multipart类型的request,也就是说当前
的 request是否有字段是"file"类型。这样做的原因是这种类型的request中getParameter()等
相类似的方法都是无法执 行的,所以要区分对待。如果是Multipart类型的,那么把这个request包
装成MultipartRequestWrapper类。
MultipartRequestWrapper 与 HttpServletRequest不同的就是重新实现了
setParameter(String name, String value),getParameter(String name),getParameterNames()
以及 getParameterValues(String name)方法。
这些方法的思想是所有的变量名、值对都保存到一个HashMap 里:key为变量名,value为变量值,但
是注意value都是String[]格式的。当有一个新的值加入的时候,通过 setParameter(...)
方法,把值加到数组的最后。在setParameter(...)中有一个比较少见的保存值的方法,记录如 下:
public void setParameter(String name, String value) {
String[] mValue = (String[]) parameters.get(name);
if (mValue == null) {
mValue = new String[0];
}
String[] newValue = new String[mValue.length + 1];
System.arraycopy(mValue, 0, newValue, 0, mValue.length);
newValue[mValue.length] = value;
parameters.put(name, newValue);
}
然后是调用processPath(HttpServletRequest, HttpServletResponse)获取地址,以这个地址作为
从 ModuleConfig获取ActionMapping的Key。这里首先查询
request.getAttribute(INCLUDE_PATH_INFO) 中是否有这样的一个值,如果为空就调用
request.getPathInfo()方法获取path。如果这样还是获取不了,就从
request.getAttribute(INCLUDE_SERVLET_PATH) 方法中获取path,找不到就使用
request.getServletPath()得到的path进行分析。分析过程如下:
然后如果这个path不是属于当前的ModuleConfig的话,直接返回null。截取prefix后面的字符串,
如果这个字符串 以.XX结尾,那么就截取"."前面的字符,比如
http://localhost:8080/servlet/where/go.do。 where为module的名称(prefix),那么我们获取的
path值为/go。
然后是通过processLocale(HttpServletRequest ,HttpServletResponse)为当前用户设定一个Local
对象,它查找的顺序 是:moduleConfig.getControllerConfig().getLocale(),
session.getAttribute(Globals.LOCALE_KEY),request.getLocale()。 你可以在config XML文件
中通过<controller><set-property..../>< /controller>执行相关的定义。
调用processContent(HttpServletRequest, HttpServletResponse)为response设定contentType。
这个contentType是从 moduleConfig.getControllerConfig().getContentType()获取的。你可以
在config XML文件中通过<controller><set-property..../></controller>执行 相关的定义。
通过processNoCache(HttpServletRequest, HttpServletResponse)方法设置response的缓存。
你可以在config XML文件中通过<controller><set-property..../></controller>执行 相关的定义。
processPreprocess(HttpServletRequest, HttpServletResponse)预处理request,这个方法是预
留的,用户可以根据自己的需要加一些预处理的程序。
通过processMapping(HttpServletRequest, HttpServletResponse, String path)以
processPath(...)的返回值为key从ModuleConfig中查找ActionMapping 对象。如果找到了,那么保
存这个Mapping到request中,key值为Globals.MAPPING_KEY。如果不存在,那么遍历 ModuleConfig
中所有的ActionMapping,查找哪一个是缺省的ActionMapping。然后把它保存到request 中,key值
为Globals.MAPPING_KEY。
通过processRoles(HttpServletRequest,HttpServletResponse,ActionMapping)检 查当前用户是
否有权限执行这个请求。如果request.isUserInRole(roles[i])返回true,则代表有。
通过processActionForm(HttpServletRequest, HttpServletResponse, ActionMapping)生成一个
ActionForm,然后根据ActionMapping所属的scope,保存到request或者 session中。key值为这个
ActionMapping中的attribute属性。ActionForm是通过 RequestUtils.createActionForm(...)方法
获取的,在下一篇将会对这个方法进行详细的说明。这里只说明 ActionForm与FormBeanConfig以及
FormPropertyConfig之间的区别。每个 FormPropertyConfig代表Form表单的一个字段,表单中的所
有FormPropertyConfig以一个HashMap保存 到FormBeanConfig中。而FormBeanConfig是在
Actionservet初始化的时候生成的:
<form-beans>
<form-bean name="一个名称,作为action选择的key" type="实际对应的ActionForm类"/>
<form-beans>
名 称用来在MoudleConfig中的formBeans HashMap中查找相应的FormBeanConfig,而FormBeanConfig
中 有一个type,它保存了上面XML文件中定义的值,用来生成ActionForm。
通过processPopulate(HttpServletRequest, HttpServletResponse, ActionForm,
ActionMapping)方法初始化ActionForm 以及 FormBean,这个方法首先会设定这个ActionForm所属
于的ActionServlet,然后对这个ActionForm进行初始 化。判断当前的reqest是否Multipart类型,
如果是就把相应的MultipartClass类全名保存到request中,key值 为Globals.MULTIPART_KEY。调
用RequestUtils.populate(...)对request中的参数进行处理, 并保存到相应的FormBean中。在下
一篇将会对这个方法进行详细的说明。最后根据request的parameter判断当前的请求是否是被 取
消,然后把相关信息保存到request中:
if ((request.getParameter(Constants.CANCEL_PROPERTY) != null) ||
(request.getParameter(Constants.CANCEL_PROPERTY_X) != null)) {
request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
}
六、下面说明这个request是如何真正被处理的,下面的方法都是在RequestProcessor中定义。
通过processValidate(HttpServletRequest, HttpServletResponse, ActionForm,
ActionMapping)判断request中的parameter是否合法。如果合法就返回true,否则取消这次 请求,
并且返回到指定的输入页面。它判断合法的过程是:如果这次的请求被取消,那么直接返回true;
如果没有要求对request中的 parameter进行合法性检验,也直接返回true;最后通过调用
form.validate(mapping, request)执行检验,如果返回的ActionErrors为null,那么代表通过
检验,返回true,否则取消这次request。取消 的过程是:如果这个请求是Multipart类型的,那么
要对这个MultipartRequestHandler进行回滚;而后,获取当前的 ActionMapping的input值,即在
config XML 中<action.../>定义的input属性。如果ActionMapping没有这个input值,那么调用
response.sendError(...) 方法,然后返回false;如果存在,就保存验证出错信息到request中,
key为Globals.ERROR_KEY,跳转到input所 指定的地址中。
processForward(HttpServletRequest, HttpServletResponse, ActionMapping)以及
processInclude(HttpServletRequest, HttpServletResponse, ActionMapping)分别通过执行
RequestDispatcher.forward(request, response) 以及 RequestDispatcher.include(request,
response)实现对页面的跳转。
但是如果当前的请求还有其它操作要执行,那么ActionMapping中的include或者forward属性值就
会为空。这时需要通 过调用processActionCreate(HttpServletRequest, HttpServletResponse,
ActionMapping) 方法根据config XML文件的配置信息生成一个Action类,然后通过
processActionPerform(...)调用生成的 Action中的execute(...)方法。最后根据execute(...)
方法返回的ActionForword类,执行跳转。在整个过 程中有一个url的计算方法,这个将在下一篇
中说明。
七、对ActionMaping结构的说明:
在ActionConfig为保存Moudle Action属性的一个javabean,它有以下属性:
* boolean configured 这个对象的所有属性是否已经被配置完。如果已经完成,那么在改变其中
任何属性都会抛出 IllegalStateException("Configuration is frozen")
* ModuleConfig moduleConfig 本ActionConfig类所属于的ModuleConfig。
* String attribute request范围 或者 session范围 的属性名称,我们将通过这个属性名称来
获取相应的form bean,注意,这个属性与form bean中定义的名称是不一样的。注意以下的方
法:
public String getAttribute() {
if (this.attribute == null) {
return (this.name);
} else {
return (this.attribute);
}
}
* String forward 调用RequestDispatcher.forward()方法时所需要的上下文相关的地址。
* String include 调用RequestDispatcher.include()方法时所需要的上下文相关的地址。
* String type Action类的类全名,如果上面的两个属性都没有定义的话,就会用它来处理
request。
* String input 如果输入数据没有通过验证,将会以这个值为地址,跳转到相应的页面。
* String multipartClass MultipartRequestHandler实现类的全名
* String name 与本类相关的 form bean 的名称。
* String parameter 用于struts的扩展,存储其它的配置信息。struts自己不会用到这个属性。
* String path 上下文相关的地址,这个地址为下一个将会进入的地址,这个地址必须要以"/"
开头。如果使用了extension mapping的话,这个地址将不会带有扩展名。
* String roles 一个以","分隔的角色名称。这些角色将会有权限访问这个request。
* String scope 缺省为session。
* String prefix,String suffix后缀和前缀。
* boolean unknown 标志当前的ActionConfig类是否是为了未知的request path而准备的,
ActionMappings将会根据这个标志返回这 个ActionConfig。
* boolean validate 是否调用validate()方法进行数据校验。
ActionMapping 继承自 ActionConfig,它增加了从ModuleConfig查找ActionForword的方法。同
时 它还提供了从ModuleConfig查找ExceptionConfig的方法,如果找不到,会通过
type.getSuperclass() 方法,根据父类的类名称继续查找,直到最后。
八、关于request.getServletPath()的解释:
request.getServletPath() 的返回值就是在下面url中定义的值,比如如果按照下面的定义
<url-pattern>
*.do
</url-pattern>
那 么:
http://localhost:8080/servlet/go.do ---------->/go.do
http://localhost:8080/servlet/where/go.do ---------->/where/go.do
这里有一个小常识,如果
<url-pattern>
/where/*.do
</url-pattern>
那 么地址中只有http://localhost:8080/servlet/where/*.do有 效(tomcat 4.0)
在ActionConfig为保存Moudle Action属性的一个javabean,它有以下属性:
* boolean configured 这个对象的所有属性是否已经被配置完。如果已经完成,那么在改变其中
任何属性都会抛出 IllegalStateException("Configuration is frozen")
* ModuleConfig moduleConfig 本ActionConfig类所属于的ModuleConfig。
* String attribute request范围 或者 session范围 的属性名称,我们将通过这个属性名称来
获取相应的form bean,注意,这个属性与form bean中定义的名称是不一样的。注意以下的方
法:
public String getAttribute() {
if (this.attribute == null) {
return (this.name);
} else {
return (this.attribute);
}
}
* String forward 调用RequestDispatcher.forward()方法时所需要的上下文相关的地址。
* String include 调用RequestDispatcher.include()方法时所需要的上下文相关的地址。
* String type Action类的类全名,如果上面的两个属性都没有定义的话,就会用它来处理
request。
* String input
* String multipartClass MultipartRequestHandler实现类的全名
* String name 与本类相关的 form bean 的名称。
* String parameter 用于struts的扩展,存储其它的配置信息。struts自己不会用到这个属性。
* String path 上下文相关的地址,这个地址为下一个将会进入的地址,这个地址必须要以"/"
开头。如果使用了extension mapping的话,这个地址将不会带有扩展名。
* String roles 一个以","分隔的角色名称。这些角色将会有权限访问这个request。
* String scope 缺省为session。
* String prefix,String suffix后缀和前缀。
* boolean unknown 标志当前的ActionConfig类是否是为了未知的request path而准备的,
ActionMappings将会根据这个标志返回这 个ActionConfig。
* boolean validate 是否调用validate()方法进行数据校验。
ActionMapping 继承自 ActionConfig,它增加了从ModuleConfig查找ActionForword的方法。同
时 它还提供了从ModuleConfig查找ExceptionConfig的方法,如果找不到,会通过
type.getSuperclass() 方法,根据父类的类名称继续查找,直到最后。
第三篇 Commons-Validator
Commons-Validator(一)
Commons-Validator包用来把验证规则程序提取出来,以供重复使用。这个包可以使 用在Struts中,也可以独立的应用在任何其它的应用中。用户可以通过java类的方式自定义验证方法,也可以在配置文件中通过正则表达式配置验证方 法。它不但支持服务器端的验证,客户端的验证也支持,具体需要使用tag把相应的js方法写入相应的页面中。
一、综述:
整个Validator框架可以有若干个FormSet,而每个FormSet又可以有若干个Form,每个Form中可以有若 干个Field。FormSet的process(...)方法,逐个调用其中的Form的process(...)方法,而Form的 process(...)方法又是逐个调用Field的process(...)方法。Validator类作为验证的起始点,调用与其一一对应的 Form的validate(...)方法,而Form的validate(...)方法又是逐个调用其中Field的validate(...)方法实 现的。
二、配置文件说明:
<form-validation>
<global>
<constant>
<constant- name>验证方法的标志名</constant-name>
<constant-value>正则表达 式</constant-value>
</constant>
<validator name="这个验证方法的标志名,供下面的depends调用"
classname="这个验证方法在哪个类中,为类全名"
method=" 验证方法的名称"
methodParams="这个验证方法需要的参数类型,依次以逗号格开,为类全名"
depends="基于什么验证 之上,可以为多个值,以逗号格开,值为方法的标志名"
jsFunction="js的方法全名,格式为文件路径.方法名。文件路径以点隔开,
如 果不填,默认为org.apache.commons.validator.javascript.xxxx"
msg="对应于 properties文件中的一条,作为不通过验证时返回的信息"/>
</global>
<formset language="语言" country="城市" variant="方言?">
<constant>
<constant- name>验证方法的标志名</constant-name>
<constant-value>正则表达 式</constant-value>
</constant>
<form name="bean 对象名称">
<field property="bean中的属性名" depends="需要什么样的验证,可以为多个值,以逗号格开,值为方法的标志名">
<arg name = "变量名" key = "properties文件的key,或者来自Var的name" resource = "是/否来自资源文件"/>
<var>
<var- name>变量名</var-name>
<var-value>变量值</var-value>
</var>
</field>
</form>
</formset>
</form-validation>
Commons-Validator(二)
在Validator的配置文件中,一共有如下几个基本元素。
一、org.apache.commons.validator.Var
它的作用是为配置文件(validator.xml)中的其它标签 提供可取用的变量,为Field提供执行验证所需要的其它参数值,比如最大长度。这个类有如下属性:name,变量的名称;value,变量的 值;jsType,当要自动生成js的时候,js的类型。
二、org.apache.commons.validator.Arg
它的作用是替换信息中的某一部分,或者为验证方法提供必需的参数 值。这个类有如下属性:bundle,资源文件名,用来存放所需要的信息。key,表示Arg的key或者value。name,表示Arg的名称。 position,这个Arg中的值用来替换信息中的哪一部分,需要替换的部分以{n}标志。resource:key所指定的信息是否来自外部的资源文 件,默认为true;如果为true,则代表key为buddle属性所指定的资源文件中的key。
三、org.apache.commons.validator.Msg
它的作用是在验证不通过时,应该返回什么的信息。这个类有如下属 性:bundle,资源文件名,用来存放所需要的信息。key,表示Msg的key或者value。name,表示Msg的名称。 resource:key所指定的信息是否来自外部的资源文件,默认为true;如果为true,则代表key为buddle属性所指定的资源文件中的 key。
四、org.apache.commons.validator.FormSet
这个类管理通过一个Map所有要检验的Form对 象,key为Form的name;同时通过一个Map管理在<formset/>中定义的Constant,key 为<constant-name>。同时其内部有language,country, variant这几个属性,用来实现国际化。一个配置文件可以有多个FormSet,这些FormSet的区别是要求不同的本地化。
五、org.apache.commons.validator.Form
这个类有如下属性:name,这个form的名称。 lFields,一个保存所有Field的List。hFields,一个保存所有Field的FastHashMap,这个FastHashMap的 key值是对应Field的key属性(对应配置文件中的property)。这个类通过validate(...)方法用来对这个Form中的所有位置 低于page的Field进行验证。它实际是在一个循环中逐个调用每个field的validate(...)方法,然后把结果保存在一个 ValidatorResults对象中。
六、org.apache.commons.validator.Field
这个类有如下属性:depends,依赖于什么验证规则。 dependencyList保存了以逗号为分隔符把depends分割生成的list。page,如果是多步提交的话当前是第几步,值等于或小于表单中 page性质的值,JavaBean 才会得到处理;args,是一个数组;这个数组的元素为HashMap,每个HashMap在数组的位置就是其中的Arg中的position属性的 值;HashMap中的key值为Arg的name,如果这个name为null则为默认的 值:DEFAULT_ARG(org.apache.commons.validator.Field.DEFAULT),value为Arg对象。 hVars,是一个FastHashMap,用来管理所有的Var对象,key为Var的name,值为Var对象。 getIndexedListProperty表明这个property为JavaBean中的一个数组元素的index。
1、process(Map globalConstants, Map constants)
这个方法用来执行配置文件中变量的替换。它 的两个参数分别为在<global/>和<formset/>中定义的constant。在这个Map中,key 为<constant/>标签中的constant-name,value为<constant-value>。在配置文件中, 可以实现如下的变量使用方式:Form的property属性,Var中的value属性,Arg中的key属性,Msg的key属性,他们均可以通过 {constant-name}的方式,引用在<global/>或者<formset/>标签中定义的constant。 Arg中的key属性,可以通过{Var:var-name}的方式引用在<var/>中定义的Var。
FormSet中的process(...)方法依次调用其中的Form的process(...)方法,而Form的process(...)方 法又依次调用其中的Field的process(...)方法。
2、validate(Map params, Map actions)
执行验证,其中actions保存了所有的 ValidatorAction对象。它首先会从params取出key为 Validator.BEAN_PARAM(java.lang.Object)的值作为要验证的JavaBean。然后通过generateKey() 方法判断当前要验证的Field是否是IndexedList。如果是,则需要分别对这个List中的各个元素进行验证;否则直接执行对与 JavaBean的特定属性(property)执行验证。
3、validateForRule(...)
接受要执行的ValidatorAction对象的同时,还是会接受Map actions参数,这是因为这个要执行的ValidatorAction可能会依赖于其它的ValidatorAction。它会先查找以前的验证结 果,如果以前没有执行过这个验证,那么执行runDependentValidators(...)方法,执行它所依赖于的 ValidatorAction;如果通过,那么再对要执行的ValidatorAction,执行验证。
Commons-Validator(三)
在Validator包中提供了一些Util类,同时提供了对基本数据类型,时间,E-mail,信用卡等格式的验证方法。
一、org.apache.commons.validator.util.Flags
这个类用来管理一系列的Flag,其中的每个 Flag的值都是2的N次方。然后每个Flag之间位与(&)就得到了整个Flags的值。
二、org.apache.commons.validator.util.ValidatorUtils
这个类为Validator提供 一些Utility的操作,共有三个方法。
1、replace(...)
这个方法用来将一个字符串中某个特性的字符串替换为另一个字符串,注意这是一个全局替换方法。
2、getValueAsString(...)
这个方法用来获取某个Bean中的一个特定的属性,然后把属性值转换为字符串返回。注 意,String[]和Collection类型的值,如果里面不包含任何值,则直接返回""。
3、copyFastHashMap(...)
很显然的是对一个特定的FashHashMap执行copy。这里要注意的是,如果 value为Msg,Arg,Var类型的时候需要执行clone,然后再保存。
三、org.apache.commons.validator.ValidatorUtil
这个类完全通过调用上面的 ValidatorUtils方法,实现自己的同名方法。只是多了一个getDelimitedRegExp(...)方法,用来生成一个正则表达式。
四、org.apache.commons.validator.DateValidator
实现了单例模式。这个类用来检查日期类型是否 合法,日期的类型要通过参数传递给这个Validator。同时还有一个strict属性,表示时候检查过渡匹配所要求的日期格式。这个日期检查就是通过 formatter.parse(value)方法检查时候有异常抛出。
五、org.apache.commons.validator.EmailValidator
实现了单例模式。这个类用来检查Email 类型是否合法。它通过正则表达式实现验证。
六、org.apache.commons.validator.GenericTypeValidator
这个类实现了对基本类型 (Byte,Short,Int,Long,Float, Date)的检测,方法很简单,就是通过每个类型的构造函数创建相应的对象,如果抛出异常就说明不符合,否则符合。同时,这个类还提供对 CreditCard验证的实现。
Commons-Validator(四)
通过调用Validator的validate(...)方法,启动验证。
一、org.apache.commons.validator.Validator
这个类是对一个Form执行验证的起点,通过调用这个 类的validate(...)方法,启动验证。这个类中的formName属性,对应于Form中的name属性。resources属性,记录这个 Validator属于哪一个ValidatorResources。parameters用来管理执行validateXXX(...)方法时所需要的 参数,它是一个Map,key为类全名,value为这个类全名所指的类的一个对象。注意,这个parameters是这个Form中所有Field所要 执行的所有validateXXX(...)方法时所需要的参数集合。每个validateXXX(...)方法,根据自己需要的参数名(类全名),从这 个parameters中查取自己所需要的参数。
二、org.apache.commons.validator.ValidatorAction
每个ValidatorAction对应 于配置文件中的一个<validator/>。它有如下属性:name,这个验正动作的标志名,用在depends属性的配置中。 classname,这个验证方法在哪个类中,为类全名。validationClass,由classname生成的Class。method,这个验 证方法的名称。validationMethod,由method生成的Method。methodParams,执行这个验证方法所需要的参数,值为逗 号隔开的类全名,默认值为 Validator.BEAN_PARAM,Validator.VALIDATOR_ACTION_PARAM,Validator.FIELD_PARAM。 parameterClasses,由methodParams生成的Class数组。depends,依赖何ValidatorAction之上。 msg,如果没有通过此验证,应该返回什么消息。javascript,为对应的javascript方法的代码内容。jsFunction,为对应的 js文件名称。jsFunctionName,对应的js中方法的名称。
1、executeValidationMethod(...)
这个方法用对Field中的值进行检测。它实际上就是通过反射,根据类名、 方法名和参数类型执行相应的验证方法。要注意的是,如果这个Field为indexList类型的,则需要把Java Bean替换为对应的Array中pos指定的元素。同时Field的key属性中的[]要变为[pos]。
2、这个类还提供了对js的基本操作,解释如下:
*generateJsFunction()
用来生成名为 org.apache.commons.validator.javascript.validateXxxx的js文件名。其中Xxxx与name相对 应。
*readJavascriptFile(...)
用来读取js文件。由于一个js文件只包含一个方法,因此会读取所有的内容后,直接返回 所读取的所有内容。
三、org.apache.commons.validator.ValidatorResults
这个类管理对一个Form中的所有 Field执行验证的结果。它有如下属性:hResults,用来管理对所有Field验证的结果,它的key为field的kye属性,它的value 为ValidatorResult对象。
四、org.apache.commons.validator.ValidatorResult
这个类用来管理对于一个Field执行验 证的时候,所有的验证方法的结果。它有如下属性:field,所要验证的Field对象。hAction,用来保存所有验证结果的Map,它的key为 ValidatorAction中的name属性,value为ResultStatus对象。
五、org.apache.commons.validator.ResultStatus
它封装了对某个Field执行某个验证后的结 果。它有如下属性:valid,表示是否通过验证。result:为执行验证后的结果。
Commons-Validator(五)
最后,说明Validator是如何初始化的。
一、org.apache.commons.validator.ValidatorResourcesInitializer
这个类用来 初始化Validator框架,通过Digester包解析用户自定义的xml配置文件。然后把解析的结果保存到ValidatorResources对 象中,最后返回这个ValidatorResources对象。
二、org.apache.commons.validator.ValidatorResources
这个类用来管理Validator 框架中的资源。它包含如下属性:hFormSets,一个FastHashMap,用来管理所有的FormSet,这个FastHashMap的key为 根据FormSet中的本地信息生成的。hConstants,一个FastHashMap,用来管理<global/>中定义的 constant。hActions,一个FastHashMap,用来管理ValidatorAction,它的key为 ValidatorAction的name属性。
三、与struts整合时需要增加的配置:
在struts-config.xml文件中加入:
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">? ? ? ? <set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml" />? ? </plug-in>
其中validator-rules.xml,为Struts自带的配置文件,配置了所有的验证方法;而validation.xml为用户自己配 置,配置要验证的对象。
四、org.apache.struts.validator.FieldChecks
这个是Struts使用的核心验证类,用来实现各种 验证规则。其中,validateXXX(...)就是想要创建的方法名,只要规则名没有重复即可。validateXXX(...)方法的参数说明:
Object 验证是在此JavaBean 对象上完成的
ValidatorAction 当前所执行的ValidatorAction
Field 被验证的字段对象
ActionErrors 如果验证失败,新增ActionError 对象的错误对象
HttpServletRequest 当前的请求对象
五、org.apache.commons.validator.ValidatorResources
这个类用来通过Arg替换Msg中 格式为{N}的字符串
六、org.apache.commons.validator.ValidatorPlugIn
这个类用来初始化Validator,然 后把初始化生成的ValidatorResources对象保存到ServletContext中,key为VALIDATOR_KEY + config.getPrefix()。
七、org.apache.struts.validator.Resources
这个类用来初始化一个Validator对象,它首先通 过key值VALIDATOR_KEY + config.getPrefix()从ServletContext取得相应的ValidatorResources对象。它再以这个对象为参数构造一 个Validator对象,然后把验证方法需要的参数保存到Validator对象的parameters属性中
第四篇 Commons-Pool包)
这些类用来生成相应的XXXObjectPool,比如 GenericKeyedObjectPoolFactory:这个类用来生成一个新的GenericKeyedObjectPool对象。它实际的作用 只是保存创建一个GenericKeyedObjectPool对象所需要的配置参数。如果使用默认的配置参数生成一个 GenericKeyedObjectPool对象,那么可以调用 GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory factory, GenericKeyedObjectPool.Config config),其中Config类里保存所有的缺省配置参数。
二、GenericKeyedObjectPool
通过_poolMap来管理相应的对象链,key作为寻找不同对象链的线索。也就是说, 一个GenericKeyedObjectPool中可以管理多个不同key的对象集合,每个集合通过_activeMap用来保存各个不同的Key当前 的活动对象的数量(闲置对象的数量可以直接通过相应的CursorableLinkedList的size来获取),可以通过 getNumActive(Object key)和getNumIdle(Object key)方法获取这
两个值。这个对象集合通过 CursorableLinkedList来管理。
这个类里的_maxIdle和_maxActive属性是针对单个key对应的CursorableLinkedList而言的最大值,而 _maxTotal属性则是对所有的key表示的对象集合的总和的最大值。_totalActive+_totalIdle用来和_maxTotal的值 进行对比,它们可以通过getNumActive()和getNumIdle()获取。_poolList存放的值与 _poolMap 中的key值一样,注意这个对象不是没有用处的,因为CursorableLinkedList是一个允许多个线程同步迭代的类。
1、几个内置的static类型的变量:
WHEN_EXHAUSTED_FAIL 这个属性表明当对象池已经用尽时(比如说池中的对象数目已经达到了最大值),borrowObject方法应该立刻抛出 NoSuchElementException异常。
WHEN_EXHAUSTED_BLOCK 这个属性表明当对象池已经用尽时(比如说池中的对象数目已经达到了最大值),borrowObject方法应该等待,直到对象池中有一个对象可以让它返 回,或者达到了最大等待时间(DEFAULT_MAX_WAIT)。
WHEN_EXHAUSTED_GROW 这个属性表明即使在对象池已经用尽时(比如说池中的对象数目已经达到了最大值),borrowObject方法应该仍旧返回一个对象。
DEFAULT_WHEN_EXHAUSTED_ACTION 这个属性表明当对象池用尽的时候(比如说池中的对象数目已经达到了最大值)应该采取上面的那种策略。
DEFAULT_MAX_IDLE 和 DEFAULT_MAX_ACTIVE分别指定了在池中对大的空闲和活动对象的数目。DEFAULT_MAX_TOTAL表明对象池允许存在的最大对象数 量。
2、CursorableLinkedList
它实际是一个双向列表,与LinkedList的区别是:它允许多个线程执行迭代。它的实 现思路是:在每次生成一个Cursor时:CursorableLinkedList.Cursor cursor(int i),都会吧这个新生成的Cursor对象包装到一个 WeakReference 中:_cursors.add( new WeakReference(cur) );在不需要的的时候需要调用Cursor. close()方法取消注册。而Cursor实际是一个以内嵌类的方式的实现了ListIterator接口的类。WeakReference 不会增加被其包装的类的引用次数,换句话说,它不会影响JVM的垃圾收集。
3、borrowObject(Object key)
这个方法用来从相应的key表示的对象集合中取出一个对象。如果对应的对象集合不存 在,会创建一个这样的集合。如果集合当前没有闲置的对象可以返回,并且当前的active < _maxActive && totalActive + _totalIdle < _maxTotal,那么通过KeyedPoolableObjectFactory创建一个新的对象;否则就根据 _whenExhaustedAction的设定执行相应的操作。要注意每次都会执行 _factory.activateObject(key,pair.value)方法初始化这个对象。在返回这个对象之前,如果设定了 _testOnBorrow标志则调用_factory.validateObject(Object key, Object obj)方法,如果不符合要求会抛出NoSuchElementException异常,否则返回这个对象。
4、returnObject(Object key, Object obj)
这个方法用来把一个对象返回给key表示的对象集合,这个 方法的执行过程与borrowObject(Object key)方法相似。它会根据_testOnReturn标志执行_factory.validateObject(Object key, Object obj)方法检查返回的对象是否合格。要注意每次都会执行 _factory.passivateObject(key, obj)方法“消除”这个对象。
5、过期类的回收:
Evictor 为一个内嵌类,实现自Runnable接口。它每过_timeBetweenEvictionRunsMillis毫秒会调用一次evict()方法,在 后台收集垃圾寿命超过_minEvictableIdleTimeMillis毫秒的对象。每当我们创建一个 GenericKeyedObjectPool对象的时候,这个类会被自动创建,并且被启动。evict() 方法很简单,它首先创建一个_evictionKeyCursor = _poolList.cursor()对象,然后根据key遍历每一个对象集合。
查 找到过期的就会删除。
三、SoftReferenceObjectPool
通过SoftReference进行对象的保存,这个SoftReference的 好处是你不需要自己进行收集,它会在JVM的内存用尽时被自动回收。这种对象池的特色是:
a、可以保存任意多个对象,不会有容量已满的情况发 生。
b、在对象池已空的时候,调用它的borrowObject方法,会自动返回新创建的实例。
c、可以在初始化同时,在池内预先创建一定量的对象。
d、当内存不足的时候,池中的对象可以被Java虚拟机回收。
四、StackObjectPool 和 StackKeyedObjectPool
通过一个Stack对对象进行管理,要注意 这个Stack是继承自Vector对象的。因此虽然线程安全,但是效率较低。这种对象池的特色是:
a、可以为对象池指定一个初始的参考大小 (当空间不够时会自动增长)。
b、在对象池已空的时候,调用它的borrowObject方法,会自动返回新创建的实例。
c、可以为对象池指定一个可保存的对象数目的上限。达到这个上限之后,再向池里送回的对象会被自动送去回收。
五、PoolableObjectFactory
Pool组件利用PoolableObjectFactory来照看被池化的对象。 ObjectPool的实例在需要处理被池化的对象的产生、激活、挂起、校验和销毁工作时,就会调用跟它关联在一起的 PoolableObjectFactory实例的相应方法来操作。 用户需要自己实现这个接口。
struts 的核心类是org.apache.struts.action.ActionServlet,这个类将会在struts第一次使用时,
作为 servlet初始化并存入tomcat容器。很显然的,初始化将会调用init方法初始化相应的数据。
一、initInternal()方法:
通过调用 MessageResources.getMessageResources(internalName)方法生成一个
MessageResources 类,getMessageResources是通过调用MessageResourcesFactory.
createResources(config) 来实现的。至于MessageResourcesFactory是一个abstract类,任何
继承自它的类都要实现 createResources方法,生成MessageResources对象。整个程序生成
MessageResourcesFactory 使用了如下技巧:
MessageResourcesFactory.factoryClass = factoryClass;
MessageResourcesFactory.clazz = null;
首先会通过factoryClass来定义一个类全名,然后通过ClassLoader.loadClass
(factoryClass) 方法来生成这个类,并赋给clazz,然后通过newInstance来生成一个对象。
在本程序中,生成MessageResources对象实 际就是对如下属性进行了初始化:
this.factory = factory;("org.apache.struts.util.PropertyMessageResourcesFactory")
this.config = config;("org.apache.struts.action.ActionResources")
this.returnNull = returnNull;(true/false)
对于MessageResources类的作用是根据不同的Locate来格式化相应的string。或者把你需要改变
的string存放 到数组中,然后通过getMessage(Locale locale, String key, Object args[])
方法来格式化。 然后把格式好的string存放到HashMap里,这样就可以为以后重用。这里的key是
使用的locale.toString() + "." + key
在PropertyMessageResources中的loadLocale方法用来读取resource的初始化信息。首先它会
通过一 个HashMap检测这个localKey相关的message是否已经被初始化了,如果被初始化过就跳
出,检测的方法是 locales.get(localeKey) != null。
然后会读取如下一个文件:
org/apache/struts /action/ActionResources_(localKey).properties,然后进行如下操作:
Properties props = new Properties();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
is = classLoader.getResourceAsStream(name);
props.load(is);
Iterator names = props.keySet().iterator();
while (names.hasNext()) {
String key = (String) names.next();
if (log.isTraceEnabled()) {
log.trace(" Saving message key '" + messageKey(localeKey, key));
}
messages.put(messageKey(localeKey, key), props.getProperty(key));
}
PropertyMessageResources 就是通过上面的loadLocale方法查找与Locale locale, String key
相对对应的Message.查找的次序如下locale.toString(),然后是
localeKey = localeKey.substring(0, underscore),然后是defaultLocale,然后是key。
最后,resource类的结构如下:
PropertyMessageResources extends MessageResources
PropertyMessageResourcesFactory extends MessageResourcesFactory
二、initOther()方法:
从servlet中获取config和debug两个参数,然后初始化ConvertUtils对象。由 于
ConvertUtils.deregister()的初始化,所有的Converter都是有初始值的,所以这里Struts自己
把 这些初始值设置为null,即转换出错的时候返回null,而不是初始值。使用ConvertUtils类的
原因是由于从form传输过来的都是 String类型的值,所以我们要把它们转换成相应的类型。
提到几个技巧:
*public boolean isIndexed() {
if (type == null) {
return (false);
//技巧一:判断是否是一个Array类的方法
} else if (type.isArray()) {
return (true);
//技巧二:判断type是否是List的一个父类或者父接口,或者与List为同一个类
//要注意如果List是另一 个primitive的TYPE类,那么type必须也是这个类才会
//返回true,否则都是false。注意long.TYPE与 Long.class是不同的
} else if (List.class.isAssignableFrom(type)) {
return (true);
} else {
return (false);
}
}
*//componentType为Array类所存储的元素的类别
Class componentType = indexedProperty.getClass().getComponentType();
//生成一个新的Array
Object newArray = Array.newInstance(componentType, (index + 1));
System.arraycopy(indexedProperty, 0, newArray, 0, length);
indexedProperty = newArray;
set(name, indexedProperty);
int newLength = Array.getLength(indexedProperty);
for (int i = length; i < newLength; i++) {
Array.set(indexedProperty, i, createProperty(name+"["+i+"]", componentType));
}
三、initServlet()方法:
这个方法主要是通过digester类解析web.xml,对String servletMapping属性进行初始化。对于
digester说明如下:这是一个基于DOM的SAX实现的类,它是事件触发的,根据xml 文件的结构,
每次读到一个节点元素就会触发一个事件。
InputStream input = getServletContext().getResourceAsStream("/WEB-INF/web.xml");
这是一个比较少见 的方法。首先通过this.servletName = getServletConfig().
getServletName()获取 servlet的名称,然后根据
if (servletName.equals(this.servletName)) {
this.servletMapping = urlPattern;
}
来判断当前读到的servlet名称是否是我们运行的servlet的名称,如果是,就把url- pattern作为
我们的servletMapping。
四、getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this)
把 自己存储到servletContext中,属性名为Globals.ACTION_SERVLET_KEY。
五、ModuleConfig moduleConfig = initModuleConfig("", config)
这个方法使用由 initOther()方法获取的config值为要解析的xml路径,用来初始化ModuleConfig。
它首先采用与生成 MessageResourcesFactory同样的方法产生一个MessageResourcesFactory对象:
MessageResourcesFactory 为一个抽象类,每一个继承它的类都要实现
createModuleConfig(String prefix)方法。本程序使用的缺省的MessageResourcesFactory类为
org.apache.struts.config.impl.DefaultModuleConfigFactory, 它
的createModuleConfig(String prefix)方法会生成一个ModuleConfigImpl类。
ModuleConfigImpl类相当于一个JavaBean,用来存放一个web模块运行时所需要的配置信息。当
然,一个web模块可 以拥有多个ModuleConfig,但是缺省的是prefix长度为0的ModuleConifg。它
的每个属性几乎都是由HashMap组成 的,它通过一个configured布尔值来描述当前的ModuleConfig
是否已经被初始化完毕,在每存放一个属性的时候都会监测这个值。 如果初始化完毕而还要改变
里面的属性值,则会报出IllegalStateException("Configuration is frozen")异常,现在对它
的属性简单说明如下:
* protected HashMap actionConfigs:
这 个HashMap用来存储ActionConfig对象。
* protected HashMap dataSources
这个 HashMap用来存储DataSourceConfig对象。
* protected HashMap exceptions
这个 HashMap用来存储ExceptionConfig对象。
* protected HashMap formBeans
这个 HashMap用来存储FormBeanConfig对象。
* protected HashMap forwards
这个 HashMap用来存储ForwardConfig对象。
* protected HashMap messageResources
这 个HashMap用来存储MessageResourcesConfig对象。
* protected ArrayList plugIns
这 个HashMap用来存储PlugInConfig对象。
* protected ControllerConfig controllerConfig
ControllerConfig类
* protected boolean configured
标 志这个ModuleConfig是(true)否(false)配置完成。
* protected String prefix
用来标志 和区分ModuleConfig类,同时在使用上面的config类初始化相应的资源以后,也是通
过这个prefix来区分所属的不同的web模 块。
* protected String actionMappingClass = "org.apache.struts.action.ActionMapping"
ActionMapping类名,缺省为 org.apache.struts.action.ActionMapping。
初始化ModuleConfig的方法如下:
首先是使用 getServletConfig().getInitParameter("mapping")来获取设定的ActionMapping类
名, 然后通过initConfigDigester()方法来生成一个digester。最后用","分隔config,对每一
块调用 parseModuleConfigFile(prefix, paths, config, digester, path)方法解析。注意,这
个 方法实际上只有两个参数是有意义的:path为我们要解析的xml文件,config用来初始化完成
后保存到servletContext中。
如果ModuleConfig中存放的FormBeanConfig为Dydamic类型,那么就调用
DynaActionFormClass.createDynaActionFormClass(FormBeanConfig) 初始化
DynaActionFormClass,并存放到DynaActionFormClass.dynaClasses 的 static HashMap中。这
里的key为FormBeanConfig.getName() + moduleConfig.getPrefix()。
如果当前的ModuleConfig为缺省的ModuleConfig,那么将会调用如下几个方法:
defaultControllerConfig(config)
defaultMessageResourcesConfig(config)
defaultFormBeansConfig(config)
defaultForwardsConfig(config)
defaultMappingsConfig(config)
在 struts1.1以后,这个特例将会被废弃:
defaultControllerConfig(config)为ControllerConfig通过getInitParameter(s) 方法初始化如
下几个属 性:bufferSize,content,locale(true/false),maxFileSize,nocache(true/false)
,multipartClass,tempDir。
defaultMessageResourcesConfig(config)为MessageResourcesConfig通过 getInitParameter(s)
方法初始化如下几个属性:application,factory,null(true/false)。
其它的几个方法就是获取不同的对象,然后把它们相应的存储到servlet中。关心如下:
ActionFormBeans=> FormBeanConfig,ActionForwards=>ForwardConfig,
ActionMappings=> ActionConfig。
六、initModuleMessageResources(ModuleConfig config)
通过存储在 ModuleConfig中的MessageResourcesConfig对象,逐个初始化MessageResource,
然后再把初始化好 的MessageResources存放到ServletContext中,attributeName为
MessageResourcesConfig.getKey() + ModuleConfig.getPrefix()。
七、initModuleDataSources(ModuleConfig config)
通过存储在ModuleConfig中的 DataSourceConfig对象,逐个初始化DataSource。然后对于每一个
DateSource通过 BeanUtils.populate(ds, dscs[i].getProperties())方法初始化其属性。再把初
始化好的 DateSource存放到ServletContext中,attributeName为
DataSourceConfig.getKey() + ModuleConfig.getPrefix()。同时也存放到名位dataSources的
FastHashMap中,key为 DataSourceConfig.getKey()。
这里还会根据生成的DateSource对象是否是GenericDataSource类型,如果是则调用
GenericDataSource.open() 方法。GenericDataSource是一个非常简单的数据库连接池,它的
open()方法用来初始化连接池,生成最小数目的 GenericConnection,这里的open()方法根据
String driver变量是否为null来判断是否已经被初始化过。需要仔细说明的是getConnection()
方法,它首先从连接池中取出 GenericConnection对象,然后检查其是否是可链接的,如果是就
返回,否则继续取出,同时activeCount-1。如果没有取 到,则会检查当前可使用的
GenericConnection是否达到最大值(activeCount < maxCount),如果没有,调用
createConnection()方法声成一个新的GenericConnection,然后检查其是否 是可链接,如果可以
则返回。returnConnection(GenericConnection conn)方法则是通过把GenericConnection放回到
连接池,然后activeCount-1。
这个方法中使用到了ServletContextWriter类,DateSource的log信息就通过这个类写入。对这个
类说明如下:
它 继承自PrintWriter,而PrintWriter又继承自Writer。Writer类所作的事情就是在同步的情况下
调用 abstract方法:abstract public void write(char cbuf[], int off, int len),这个方法
将会根据调用者的需要由调用者实现。
PrintWriter则首先通过ensureOpen()方法检验这个类中是否 有写入的对象(Writer类或其子类),
如果有则根据不同的情况调用这个写入对象的write方法(out.write(....))。这个类 的print(...)
方法就是据不同的情况调用相应的write(...)方法。而println(...)与之的区别就是每次多写入一
个 换行字符串。还有一个区别是println(...)会根据是否需要autoflush进行flush,而write(...)
方法不会。
ServletContextWriter 类的作用是把字符写入ServletContext中。ServletContextWriter类方法中
真正实现了write方法:
public void write(char c) {
if (c == '\n')
flush();
else if (c != '\r')
buffer.append(c);
}
public void flush() {
if (buffer.length() > 0) {
context.log(buffer.toString());
buffer.setLength(0);
}
}
八、initModulePlugIns(moduleConfig)
通过存储在ModuleConfig中的PlugInConfig对 象,逐个初始化PlugIn对象,存放到一个数组中,
然后再把这个数组存放到ServletContext中,attributeName为
Globals.PLUG_INS_KEY + ModuleConfig.getPrefix()。
对每一个生成的PlugIn对象通过
BeanUtils.populate(plugIns[i], plugInConfigs[i].getProperties())方法初始化其属性。然后
再把PlugInConfig对象存放到由其生成的 PlugIn对象中。
最后,通过plugIns[i].init(this, (ModuleConfig) config)初始化这个plugIn对象。
九、初始化结束
完成了这个初始化以后,会调用ModuleConfig.freeze()令这个ModuleConfig变得不可改变。然 后
会遍历ServletConfig中的initParameterNames,如果有以"config/"开头的,则通过这个 parameter
的值继续初始化其它的ModuleConfig,且这个ModuleConfig的prefix为"config/"后的字符 串。
同样调用如下方法:
initModuleMessageResources(moduleConfig);
initModuleDataSources(moduleConfig);
initModulePlugIns(moduleConfig);
moduleConfig.freeze();
最后调用destroyConfigDigester()释放内存。
第二篇 struts的执行
本篇详细介绍struts在初始化之后是如何处理一个请求,并返回数据的。这里最核心 的类是
RequestProcessor以及RequestUtils。RequestProcessor类通过 RequestDispatcher实现页面的跳转,
而RequestProcessor负责处理request中传来的请求信息,存放到 FormBeanConfig中,以及对要跳转的
url进行处理。
struts 在初始化完成之后,会根据请求调用doGet(...)或者doPost(...)方法,这两个方法直接
调用 process(request, response)方法。process(...)方法首先判断当前的request属于
哪一个 ModuleConfig,然后生成与这个ModuleConifg相对应的RequestProcessor,最后调用这个
RequestProcessor 的process(...)方法,执行request的请求。
一、RequestUtils.selectModule(String prefix, HttpServletRequest,ServletContext)方法:
这个方法,根据prefix,从ServletContext中选 择相应的ModuleConfig,然后把这个ModuleConfig
保存到request中。ServletContext对应的key值为 Globals.MODULE_KEY + prefix,保存到request
中使用的key值为Globals.MODULE_KEY。如果 在ServletContext中不存在这样的一个ModuleConfig,
那么调用 request.removeAttribute(Globals.MODULE_KEY)方法。然后以同样的方法查找、保存
MessageResources 对象。当prefix为空时,会调用下面的方法选择ModuleConfig。
二、RequestUtils.selectModule(HttpServletRequest, ServletContext)
这个 方发首先使用getModuleName(HttpServletRequest, ServletContext)获取相应的path,然后
通 过调用getModuleName(String matchPath, ServletContext)获取相应的prefix。以这prefix为
参 数调用(一)中的selectModule(...)选择ModuleConfig。
获取path的过程为:首先从request中查找名称为
INCLUDE_SERVLET_PATH(javax.servlet.include.servlet_path) 的属性,如果为空,就调用
request.getServletPath()方法获取servletPath。
获取prefix的过程为:它首先调用getModulePrefixes(ServletContext),获取所有已存在的
module 前缀。 然后通过分析上面获取的path来判断当前的url属于哪一个module,方法是截取
path中最后一个"/"字符前面的字符串,然后 与有上面方法中获取的prefixes[]逐个对比,如果
prefixes[]中存在这样的一个值,则返回这个截取的字符串,否则继续截取 path最后面的"/"前面
的字符串,然后对比。如果始终找不到,则返回""。
getModulePrefixes(ServletContext)的执行过程是:首先通过
context.getAttribute(PREFIXES_KEY) 查找是否存在这样的一个保存所有的prefix的string array,
如果存在,就说明已经解析过一次了,就直接返回这个string array,否则遍历ServletContext中
所有的attribute name,查找以MODULE_KEY(org.apache.struts.action.MODULE)开头的
atrribute name,然后截取这个atrribute name中MODULE_KEY后面的字符串,作为一个prefix. 最
后把通过这个这个方式获取 的所有的prefix作为一个ArrayList存储到ServletContext中,属性key
值为 PREFIXES_KEY(org.apache.struts.util.PREFIXES),这样下次再次查找的时候就可以直接从这
个 attribute中获取了。
三、getModuleConfig(HttpServletRequest)
这个方法就是获取上面的selectModule(...) 方法所得到的ModuleConfig。如果找不到这样的
ModuleConfig,那么就把ServletContext中缺省的 ModuleConfig返回(调用
getServletContext().getAttribute(Globals.MODULE_KEY))
四、getRequestProcessor(ModuleConfig config)
这个方法从根据ModuleConfig的 prefix作为key,从ServletContext中获取RequestProcessor。这
个key值为 Globals.REQUEST_PROCESSOR_KEY + config.getPrefix()。
如果ServletContext中不存在这样的一个RequestProcessor,那么就生成一个新的
RequestProcessor 的实例,完成初始化(保存ActionServlet以及ModuleConfig到这个新的实例中)后
将其保存到ServletContext 中。
五、RequestProcessor.process(HttpServletRequest, HttpServletResponse)
这 是真正执行HttpServletRequst请求的方法。
这个方法首先判断但前的HttpServletRequest是否是Multipart类型的request,也就是说当前
的 request是否有字段是"file"类型。这样做的原因是这种类型的request中getParameter()等
相类似的方法都是无法执 行的,所以要区分对待。如果是Multipart类型的,那么把这个request包
装成MultipartRequestWrapper类。
MultipartRequestWrapper 与 HttpServletRequest不同的就是重新实现了
setParameter(String name, String value),getParameter(String name),getParameterNames()
以及 getParameterValues(String name)方法。
这些方法的思想是所有的变量名、值对都保存到一个HashMap 里:key为变量名,value为变量值,但
是注意value都是String[]格式的。当有一个新的值加入的时候,通过 setParameter(...)
方法,把值加到数组的最后。在setParameter(...)中有一个比较少见的保存值的方法,记录如 下:
public void setParameter(String name, String value) {
String[] mValue = (String[]) parameters.get(name);
if (mValue == null) {
mValue = new String[0];
}
String[] newValue = new String[mValue.length + 1];
System.arraycopy(mValue, 0, newValue, 0, mValue.length);
newValue[mValue.length] = value;
parameters.put(name, newValue);
}
然后是调用processPath(HttpServletRequest, HttpServletResponse)获取地址,以这个地址作为
从 ModuleConfig获取ActionMapping的Key。这里首先查询
request.getAttribute(INCLUDE_PATH_INFO) 中是否有这样的一个值,如果为空就调用
request.getPathInfo()方法获取path。如果这样还是获取不了,就从
request.getAttribute(INCLUDE_SERVLET_PATH) 方法中获取path,找不到就使用
request.getServletPath()得到的path进行分析。分析过程如下:
然后如果这个path不是属于当前的ModuleConfig的话,直接返回null。截取prefix后面的字符串,
如果这个字符串 以.XX结尾,那么就截取"."前面的字符,比如
http://localhost:8080/servlet/where/go.do。 where为module的名称(prefix),那么我们获取的
path值为/go。
然后是通过processLocale(HttpServletRequest ,HttpServletResponse)为当前用户设定一个Local
对象,它查找的顺序 是:moduleConfig.getControllerConfig().getLocale(),
session.getAttribute(Globals.LOCALE_KEY),request.getLocale()。 你可以在config XML文件
中通过<controller><set-property..../>< /controller>执行相关的定义。
调用processContent(HttpServletRequest, HttpServletResponse)为response设定contentType。
这个contentType是从 moduleConfig.getControllerConfig().getContentType()获取的。你可以
在config XML文件中通过<controller><set-property..../></controller>执行 相关的定义。
通过processNoCache(HttpServletRequest, HttpServletResponse)方法设置response的缓存。
你可以在config XML文件中通过<controller><set-property..../></controller>执行 相关的定义。
processPreprocess(HttpServletRequest, HttpServletResponse)预处理request,这个方法是预
留的,用户可以根据自己的需要加一些预处理的程序。
通过processMapping(HttpServletRequest, HttpServletResponse, String path)以
processPath(...)的返回值为key从ModuleConfig中查找ActionMapping 对象。如果找到了,那么保
存这个Mapping到request中,key值为Globals.MAPPING_KEY。如果不存在,那么遍历 ModuleConfig
中所有的ActionMapping,查找哪一个是缺省的ActionMapping。然后把它保存到request 中,key值
为Globals.MAPPING_KEY。
通过processRoles(HttpServletRequest,HttpServletResponse,ActionMapping)检 查当前用户是
否有权限执行这个请求。如果request.isUserInRole(roles[i])返回true,则代表有。
通过processActionForm(HttpServletRequest, HttpServletResponse, ActionMapping)生成一个
ActionForm,然后根据ActionMapping所属的scope,保存到request或者 session中。key值为这个
ActionMapping中的attribute属性。ActionForm是通过 RequestUtils.createActionForm(...)方法
获取的,在下一篇将会对这个方法进行详细的说明。这里只说明 ActionForm与FormBeanConfig以及
FormPropertyConfig之间的区别。每个 FormPropertyConfig代表Form表单的一个字段,表单中的所
有FormPropertyConfig以一个HashMap保存 到FormBeanConfig中。而FormBeanConfig是在
Actionservet初始化的时候生成的:
<form-beans>
<form-bean name="一个名称,作为action选择的key" type="实际对应的ActionForm类"/>
<form-beans>
名 称用来在MoudleConfig中的formBeans HashMap中查找相应的FormBeanConfig,而FormBeanConfig
中 有一个type,它保存了上面XML文件中定义的值,用来生成ActionForm。
通过processPopulate(HttpServletRequest, HttpServletResponse, ActionForm,
ActionMapping)方法初始化ActionForm 以及 FormBean,这个方法首先会设定这个ActionForm所属
于的ActionServlet,然后对这个ActionForm进行初始 化。判断当前的reqest是否Multipart类型,
如果是就把相应的MultipartClass类全名保存到request中,key值 为Globals.MULTIPART_KEY。调
用RequestUtils.populate(...)对request中的参数进行处理, 并保存到相应的FormBean中。在下
一篇将会对这个方法进行详细的说明。最后根据request的parameter判断当前的请求是否是被 取
消,然后把相关信息保存到request中:
if ((request.getParameter(Constants.CANCEL_PROPERTY) != null) ||
(request.getParameter(Constants.CANCEL_PROPERTY_X) != null)) {
request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE);
}
六、下面说明这个request是如何真正被处理的,下面的方法都是在RequestProcessor中定义。
通过processValidate(HttpServletRequest, HttpServletResponse, ActionForm,
ActionMapping)判断request中的parameter是否合法。如果合法就返回true,否则取消这次 请求,
并且返回到指定的输入页面。它判断合法的过程是:如果这次的请求被取消,那么直接返回true;
如果没有要求对request中的 parameter进行合法性检验,也直接返回true;最后通过调用
form.validate(mapping, request)执行检验,如果返回的ActionErrors为null,那么代表通过
检验,返回true,否则取消这次request。取消 的过程是:如果这个请求是Multipart类型的,那么
要对这个MultipartRequestHandler进行回滚;而后,获取当前的 ActionMapping的input值,即在
config XML 中<action.../>定义的input属性。如果ActionMapping没有这个input值,那么调用
response.sendError(...) 方法,然后返回false;如果存在,就保存验证出错信息到request中,
key为Globals.ERROR_KEY,跳转到input所 指定的地址中。
processForward(HttpServletRequest, HttpServletResponse, ActionMapping)以及
processInclude(HttpServletRequest, HttpServletResponse, ActionMapping)分别通过执行
RequestDispatcher.forward(request, response) 以及 RequestDispatcher.include(request,
response)实现对页面的跳转。
但是如果当前的请求还有其它操作要执行,那么ActionMapping中的include或者forward属性值就
会为空。这时需要通 过调用processActionCreate(HttpServletRequest, HttpServletResponse,
ActionMapping) 方法根据config XML文件的配置信息生成一个Action类,然后通过
processActionPerform(...)调用生成的 Action中的execute(...)方法。最后根据execute(...)
方法返回的ActionForword类,执行跳转。在整个过 程中有一个url的计算方法,这个将在下一篇
中说明。
七、对ActionMaping结构的说明:
在ActionConfig为保存Moudle Action属性的一个javabean,它有以下属性:
* boolean configured 这个对象的所有属性是否已经被配置完。如果已经完成,那么在改变其中
任何属性都会抛出 IllegalStateException("Configuration is frozen")
* ModuleConfig moduleConfig 本ActionConfig类所属于的ModuleConfig。
* String attribute request范围 或者 session范围 的属性名称,我们将通过这个属性名称来
获取相应的form bean,注意,这个属性与form bean中定义的名称是不一样的。注意以下的方
法:
public String getAttribute() {
if (this.attribute == null) {
return (this.name);
} else {
return (this.attribute);
}
}
* String forward 调用RequestDispatcher.forward()方法时所需要的上下文相关的地址。
* String include 调用RequestDispatcher.include()方法时所需要的上下文相关的地址。
* String type Action类的类全名,如果上面的两个属性都没有定义的话,就会用它来处理
request。
* String input 如果输入数据没有通过验证,将会以这个值为地址,跳转到相应的页面。
* String multipartClass MultipartRequestHandler实现类的全名
* String name 与本类相关的 form bean 的名称。
* String parameter 用于struts的扩展,存储其它的配置信息。struts自己不会用到这个属性。
* String path 上下文相关的地址,这个地址为下一个将会进入的地址,这个地址必须要以"/"
开头。如果使用了extension mapping的话,这个地址将不会带有扩展名。
* String roles 一个以","分隔的角色名称。这些角色将会有权限访问这个request。
* String scope 缺省为session。
* String prefix,String suffix后缀和前缀。
* boolean unknown 标志当前的ActionConfig类是否是为了未知的request path而准备的,
ActionMappings将会根据这个标志返回这 个ActionConfig。
* boolean validate 是否调用validate()方法进行数据校验。
ActionMapping 继承自 ActionConfig,它增加了从ModuleConfig查找ActionForword的方法。同
时 它还提供了从ModuleConfig查找ExceptionConfig的方法,如果找不到,会通过
type.getSuperclass() 方法,根据父类的类名称继续查找,直到最后。
八、关于request.getServletPath()的解释:
request.getServletPath() 的返回值就是在下面url中定义的值,比如如果按照下面的定义
<url-pattern>
*.do
</url-pattern>
那 么:
http://localhost:8080/servlet/go.do ---------->/go.do
http://localhost:8080/servlet/where/go.do ---------->/where/go.do
这里有一个小常识,如果
<url-pattern>
/where/*.do
</url-pattern>
那 么地址中只有http://localhost:8080/servlet/where/*.do有 效(tomcat 4.0)
在ActionConfig为保存Moudle Action属性的一个javabean,它有以下属性:
* boolean configured 这个对象的所有属性是否已经被配置完。如果已经完成,那么在改变其中
任何属性都会抛出 IllegalStateException("Configuration is frozen")
* ModuleConfig moduleConfig 本ActionConfig类所属于的ModuleConfig。
* String attribute request范围 或者 session范围 的属性名称,我们将通过这个属性名称来
获取相应的form bean,注意,这个属性与form bean中定义的名称是不一样的。注意以下的方
法:
public String getAttribute() {
if (this.attribute == null) {
return (this.name);
} else {
return (this.attribute);
}
}
* String forward 调用RequestDispatcher.forward()方法时所需要的上下文相关的地址。
* String include 调用RequestDispatcher.include()方法时所需要的上下文相关的地址。
* String type Action类的类全名,如果上面的两个属性都没有定义的话,就会用它来处理
request。
* String input
* String multipartClass MultipartRequestHandler实现类的全名
* String name 与本类相关的 form bean 的名称。
* String parameter 用于struts的扩展,存储其它的配置信息。struts自己不会用到这个属性。
* String path 上下文相关的地址,这个地址为下一个将会进入的地址,这个地址必须要以"/"
开头。如果使用了extension mapping的话,这个地址将不会带有扩展名。
* String roles 一个以","分隔的角色名称。这些角色将会有权限访问这个request。
* String scope 缺省为session。
* String prefix,String suffix后缀和前缀。
* boolean unknown 标志当前的ActionConfig类是否是为了未知的request path而准备的,
ActionMappings将会根据这个标志返回这 个ActionConfig。
* boolean validate 是否调用validate()方法进行数据校验。
ActionMapping 继承自 ActionConfig,它增加了从ModuleConfig查找ActionForword的方法。同
时 它还提供了从ModuleConfig查找ExceptionConfig的方法,如果找不到,会通过
type.getSuperclass() 方法,根据父类的类名称继续查找,直到最后。
第三篇 Commons-Validator
Commons-Validator(一)
Commons-Validator包用来把验证规则程序提取出来,以供重复使用。这个包可以使 用在Struts中,也可以独立的应用在任何其它的应用中。用户可以通过java类的方式自定义验证方法,也可以在配置文件中通过正则表达式配置验证方 法。它不但支持服务器端的验证,客户端的验证也支持,具体需要使用tag把相应的js方法写入相应的页面中。
一、综述:
整个Validator框架可以有若干个FormSet,而每个FormSet又可以有若干个Form,每个Form中可以有若 干个Field。FormSet的process(...)方法,逐个调用其中的Form的process(...)方法,而Form的 process(...)方法又是逐个调用Field的process(...)方法。Validator类作为验证的起始点,调用与其一一对应的 Form的validate(...)方法,而Form的validate(...)方法又是逐个调用其中Field的validate(...)方法实 现的。
二、配置文件说明:
<form-validation>
<global>
<constant>
<constant- name>验证方法的标志名</constant-name>
<constant-value>正则表达 式</constant-value>
</constant>
<validator name="这个验证方法的标志名,供下面的depends调用"
classname="这个验证方法在哪个类中,为类全名"
method=" 验证方法的名称"
methodParams="这个验证方法需要的参数类型,依次以逗号格开,为类全名"
depends="基于什么验证 之上,可以为多个值,以逗号格开,值为方法的标志名"
jsFunction="js的方法全名,格式为文件路径.方法名。文件路径以点隔开,
如 果不填,默认为org.apache.commons.validator.javascript.xxxx"
msg="对应于 properties文件中的一条,作为不通过验证时返回的信息"/>
</global>
<formset language="语言" country="城市" variant="方言?">
<constant>
<constant- name>验证方法的标志名</constant-name>
<constant-value>正则表达 式</constant-value>
</constant>
<form name="bean 对象名称">
<field property="bean中的属性名" depends="需要什么样的验证,可以为多个值,以逗号格开,值为方法的标志名">
<arg name = "变量名" key = "properties文件的key,或者来自Var的name" resource = "是/否来自资源文件"/>
<var>
<var- name>变量名</var-name>
<var-value>变量值</var-value>
</var>
</field>
</form>
</formset>
</form-validation>
Commons-Validator(二)
在Validator的配置文件中,一共有如下几个基本元素。
一、org.apache.commons.validator.Var
它的作用是为配置文件(validator.xml)中的其它标签 提供可取用的变量,为Field提供执行验证所需要的其它参数值,比如最大长度。这个类有如下属性:name,变量的名称;value,变量的 值;jsType,当要自动生成js的时候,js的类型。
二、org.apache.commons.validator.Arg
它的作用是替换信息中的某一部分,或者为验证方法提供必需的参数 值。这个类有如下属性:bundle,资源文件名,用来存放所需要的信息。key,表示Arg的key或者value。name,表示Arg的名称。 position,这个Arg中的值用来替换信息中的哪一部分,需要替换的部分以{n}标志。resource:key所指定的信息是否来自外部的资源文 件,默认为true;如果为true,则代表key为buddle属性所指定的资源文件中的key。
三、org.apache.commons.validator.Msg
它的作用是在验证不通过时,应该返回什么的信息。这个类有如下属 性:bundle,资源文件名,用来存放所需要的信息。key,表示Msg的key或者value。name,表示Msg的名称。 resource:key所指定的信息是否来自外部的资源文件,默认为true;如果为true,则代表key为buddle属性所指定的资源文件中的 key。
四、org.apache.commons.validator.FormSet
这个类管理通过一个Map所有要检验的Form对 象,key为Form的name;同时通过一个Map管理在<formset/>中定义的Constant,key 为<constant-name>。同时其内部有language,country, variant这几个属性,用来实现国际化。一个配置文件可以有多个FormSet,这些FormSet的区别是要求不同的本地化。
五、org.apache.commons.validator.Form
这个类有如下属性:name,这个form的名称。 lFields,一个保存所有Field的List。hFields,一个保存所有Field的FastHashMap,这个FastHashMap的 key值是对应Field的key属性(对应配置文件中的property)。这个类通过validate(...)方法用来对这个Form中的所有位置 低于page的Field进行验证。它实际是在一个循环中逐个调用每个field的validate(...)方法,然后把结果保存在一个 ValidatorResults对象中。
六、org.apache.commons.validator.Field
这个类有如下属性:depends,依赖于什么验证规则。 dependencyList保存了以逗号为分隔符把depends分割生成的list。page,如果是多步提交的话当前是第几步,值等于或小于表单中 page性质的值,JavaBean 才会得到处理;args,是一个数组;这个数组的元素为HashMap,每个HashMap在数组的位置就是其中的Arg中的position属性的 值;HashMap中的key值为Arg的name,如果这个name为null则为默认的 值:DEFAULT_ARG(org.apache.commons.validator.Field.DEFAULT),value为Arg对象。 hVars,是一个FastHashMap,用来管理所有的Var对象,key为Var的name,值为Var对象。 getIndexedListProperty表明这个property为JavaBean中的一个数组元素的index。
1、process(Map globalConstants, Map constants)
这个方法用来执行配置文件中变量的替换。它 的两个参数分别为在<global/>和<formset/>中定义的constant。在这个Map中,key 为<constant/>标签中的constant-name,value为<constant-value>。在配置文件中, 可以实现如下的变量使用方式:Form的property属性,Var中的value属性,Arg中的key属性,Msg的key属性,他们均可以通过 {constant-name}的方式,引用在<global/>或者<formset/>标签中定义的constant。 Arg中的key属性,可以通过{Var:var-name}的方式引用在<var/>中定义的Var。
FormSet中的process(...)方法依次调用其中的Form的process(...)方法,而Form的process(...)方 法又依次调用其中的Field的process(...)方法。
2、validate(Map params, Map actions)
执行验证,其中actions保存了所有的 ValidatorAction对象。它首先会从params取出key为 Validator.BEAN_PARAM(java.lang.Object)的值作为要验证的JavaBean。然后通过generateKey() 方法判断当前要验证的Field是否是IndexedList。如果是,则需要分别对这个List中的各个元素进行验证;否则直接执行对与 JavaBean的特定属性(property)执行验证。
3、validateForRule(...)
接受要执行的ValidatorAction对象的同时,还是会接受Map actions参数,这是因为这个要执行的ValidatorAction可能会依赖于其它的ValidatorAction。它会先查找以前的验证结 果,如果以前没有执行过这个验证,那么执行runDependentValidators(...)方法,执行它所依赖于的 ValidatorAction;如果通过,那么再对要执行的ValidatorAction,执行验证。
Commons-Validator(三)
在Validator包中提供了一些Util类,同时提供了对基本数据类型,时间,E-mail,信用卡等格式的验证方法。
一、org.apache.commons.validator.util.Flags
这个类用来管理一系列的Flag,其中的每个 Flag的值都是2的N次方。然后每个Flag之间位与(&)就得到了整个Flags的值。
二、org.apache.commons.validator.util.ValidatorUtils
这个类为Validator提供 一些Utility的操作,共有三个方法。
1、replace(...)
这个方法用来将一个字符串中某个特性的字符串替换为另一个字符串,注意这是一个全局替换方法。
2、getValueAsString(...)
这个方法用来获取某个Bean中的一个特定的属性,然后把属性值转换为字符串返回。注 意,String[]和Collection类型的值,如果里面不包含任何值,则直接返回""。
3、copyFastHashMap(...)
很显然的是对一个特定的FashHashMap执行copy。这里要注意的是,如果 value为Msg,Arg,Var类型的时候需要执行clone,然后再保存。
三、org.apache.commons.validator.ValidatorUtil
这个类完全通过调用上面的 ValidatorUtils方法,实现自己的同名方法。只是多了一个getDelimitedRegExp(...)方法,用来生成一个正则表达式。
四、org.apache.commons.validator.DateValidator
实现了单例模式。这个类用来检查日期类型是否 合法,日期的类型要通过参数传递给这个Validator。同时还有一个strict属性,表示时候检查过渡匹配所要求的日期格式。这个日期检查就是通过 formatter.parse(value)方法检查时候有异常抛出。
五、org.apache.commons.validator.EmailValidator
实现了单例模式。这个类用来检查Email 类型是否合法。它通过正则表达式实现验证。
六、org.apache.commons.validator.GenericTypeValidator
这个类实现了对基本类型 (Byte,Short,Int,Long,Float, Date)的检测,方法很简单,就是通过每个类型的构造函数创建相应的对象,如果抛出异常就说明不符合,否则符合。同时,这个类还提供对 CreditCard验证的实现。
Commons-Validator(四)
通过调用Validator的validate(...)方法,启动验证。
一、org.apache.commons.validator.Validator
这个类是对一个Form执行验证的起点,通过调用这个 类的validate(...)方法,启动验证。这个类中的formName属性,对应于Form中的name属性。resources属性,记录这个 Validator属于哪一个ValidatorResources。parameters用来管理执行validateXXX(...)方法时所需要的 参数,它是一个Map,key为类全名,value为这个类全名所指的类的一个对象。注意,这个parameters是这个Form中所有Field所要 执行的所有validateXXX(...)方法时所需要的参数集合。每个validateXXX(...)方法,根据自己需要的参数名(类全名),从这 个parameters中查取自己所需要的参数。
二、org.apache.commons.validator.ValidatorAction
每个ValidatorAction对应 于配置文件中的一个<validator/>。它有如下属性:name,这个验正动作的标志名,用在depends属性的配置中。 classname,这个验证方法在哪个类中,为类全名。validationClass,由classname生成的Class。method,这个验 证方法的名称。validationMethod,由method生成的Method。methodParams,执行这个验证方法所需要的参数,值为逗 号隔开的类全名,默认值为 Validator.BEAN_PARAM,Validator.VALIDATOR_ACTION_PARAM,Validator.FIELD_PARAM。 parameterClasses,由methodParams生成的Class数组。depends,依赖何ValidatorAction之上。 msg,如果没有通过此验证,应该返回什么消息。javascript,为对应的javascript方法的代码内容。jsFunction,为对应的 js文件名称。jsFunctionName,对应的js中方法的名称。
1、executeValidationMethod(...)
这个方法用对Field中的值进行检测。它实际上就是通过反射,根据类名、 方法名和参数类型执行相应的验证方法。要注意的是,如果这个Field为indexList类型的,则需要把Java Bean替换为对应的Array中pos指定的元素。同时Field的key属性中的[]要变为[pos]。
2、这个类还提供了对js的基本操作,解释如下:
*generateJsFunction()
用来生成名为 org.apache.commons.validator.javascript.validateXxxx的js文件名。其中Xxxx与name相对 应。
*readJavascriptFile(...)
用来读取js文件。由于一个js文件只包含一个方法,因此会读取所有的内容后,直接返回 所读取的所有内容。
三、org.apache.commons.validator.ValidatorResults
这个类管理对一个Form中的所有 Field执行验证的结果。它有如下属性:hResults,用来管理对所有Field验证的结果,它的key为field的kye属性,它的value 为ValidatorResult对象。
四、org.apache.commons.validator.ValidatorResult
这个类用来管理对于一个Field执行验 证的时候,所有的验证方法的结果。它有如下属性:field,所要验证的Field对象。hAction,用来保存所有验证结果的Map,它的key为 ValidatorAction中的name属性,value为ResultStatus对象。
五、org.apache.commons.validator.ResultStatus
它封装了对某个Field执行某个验证后的结 果。它有如下属性:valid,表示是否通过验证。result:为执行验证后的结果。
Commons-Validator(五)
最后,说明Validator是如何初始化的。
一、org.apache.commons.validator.ValidatorResourcesInitializer
这个类用来 初始化Validator框架,通过Digester包解析用户自定义的xml配置文件。然后把解析的结果保存到ValidatorResources对 象中,最后返回这个ValidatorResources对象。
二、org.apache.commons.validator.ValidatorResources
这个类用来管理Validator 框架中的资源。它包含如下属性:hFormSets,一个FastHashMap,用来管理所有的FormSet,这个FastHashMap的key为 根据FormSet中的本地信息生成的。hConstants,一个FastHashMap,用来管理<global/>中定义的 constant。hActions,一个FastHashMap,用来管理ValidatorAction,它的key为 ValidatorAction的name属性。
三、与struts整合时需要增加的配置:
在struts-config.xml文件中加入:
<plug-in className="org.apache.struts.validator.ValidatorPlugIn">? ? ? ? <set-property property="pathnames" value="/WEB-INF/validator-rules.xml,/WEB-INF/validation.xml" />? ? </plug-in>
其中validator-rules.xml,为Struts自带的配置文件,配置了所有的验证方法;而validation.xml为用户自己配 置,配置要验证的对象。
四、org.apache.struts.validator.FieldChecks
这个是Struts使用的核心验证类,用来实现各种 验证规则。其中,validateXXX(...)就是想要创建的方法名,只要规则名没有重复即可。validateXXX(...)方法的参数说明:
Object 验证是在此JavaBean 对象上完成的
ValidatorAction 当前所执行的ValidatorAction
Field 被验证的字段对象
ActionErrors 如果验证失败,新增ActionError 对象的错误对象
HttpServletRequest 当前的请求对象
五、org.apache.commons.validator.ValidatorResources
这个类用来通过Arg替换Msg中 格式为{N}的字符串
六、org.apache.commons.validator.ValidatorPlugIn
这个类用来初始化Validator,然 后把初始化生成的ValidatorResources对象保存到ServletContext中,key为VALIDATOR_KEY + config.getPrefix()。
七、org.apache.struts.validator.Resources
这个类用来初始化一个Validator对象,它首先通 过key值VALIDATOR_KEY + config.getPrefix()从ServletContext取得相应的ValidatorResources对象。它再以这个对象为参数构造一 个Validator对象,然后把验证方法需要的参数保存到Validator对象的parameters属性中
第四篇 Commons-Pool包)
这些类用来生成相应的XXXObjectPool,比如 GenericKeyedObjectPoolFactory:这个类用来生成一个新的GenericKeyedObjectPool对象。它实际的作用 只是保存创建一个GenericKeyedObjectPool对象所需要的配置参数。如果使用默认的配置参数生成一个 GenericKeyedObjectPool对象,那么可以调用 GenericKeyedObjectPoolFactory(KeyedPoolableObjectFactory factory, GenericKeyedObjectPool.Config config),其中Config类里保存所有的缺省配置参数。
二、GenericKeyedObjectPool
通过_poolMap来管理相应的对象链,key作为寻找不同对象链的线索。也就是说, 一个GenericKeyedObjectPool中可以管理多个不同key的对象集合,每个集合通过_activeMap用来保存各个不同的Key当前 的活动对象的数量(闲置对象的数量可以直接通过相应的CursorableLinkedList的size来获取),可以通过 getNumActive(Object key)和getNumIdle(Object key)方法获取这
两个值。这个对象集合通过 CursorableLinkedList来管理。
这个类里的_maxIdle和_maxActive属性是针对单个key对应的CursorableLinkedList而言的最大值,而 _maxTotal属性则是对所有的key表示的对象集合的总和的最大值。_totalActive+_totalIdle用来和_maxTotal的值 进行对比,它们可以通过getNumActive()和getNumIdle()获取。_poolList存放的值与 _poolMap 中的key值一样,注意这个对象不是没有用处的,因为CursorableLinkedList是一个允许多个线程同步迭代的类。
1、几个内置的static类型的变量:
WHEN_EXHAUSTED_FAIL 这个属性表明当对象池已经用尽时(比如说池中的对象数目已经达到了最大值),borrowObject方法应该立刻抛出 NoSuchElementException异常。
WHEN_EXHAUSTED_BLOCK 这个属性表明当对象池已经用尽时(比如说池中的对象数目已经达到了最大值),borrowObject方法应该等待,直到对象池中有一个对象可以让它返 回,或者达到了最大等待时间(DEFAULT_MAX_WAIT)。
WHEN_EXHAUSTED_GROW 这个属性表明即使在对象池已经用尽时(比如说池中的对象数目已经达到了最大值),borrowObject方法应该仍旧返回一个对象。
DEFAULT_WHEN_EXHAUSTED_ACTION 这个属性表明当对象池用尽的时候(比如说池中的对象数目已经达到了最大值)应该采取上面的那种策略。
DEFAULT_MAX_IDLE 和 DEFAULT_MAX_ACTIVE分别指定了在池中对大的空闲和活动对象的数目。DEFAULT_MAX_TOTAL表明对象池允许存在的最大对象数 量。
2、CursorableLinkedList
它实际是一个双向列表,与LinkedList的区别是:它允许多个线程执行迭代。它的实 现思路是:在每次生成一个Cursor时:CursorableLinkedList.Cursor cursor(int i),都会吧这个新生成的Cursor对象包装到一个 WeakReference 中:_cursors.add( new WeakReference(cur) );在不需要的的时候需要调用Cursor. close()方法取消注册。而Cursor实际是一个以内嵌类的方式的实现了ListIterator接口的类。WeakReference 不会增加被其包装的类的引用次数,换句话说,它不会影响JVM的垃圾收集。
3、borrowObject(Object key)
这个方法用来从相应的key表示的对象集合中取出一个对象。如果对应的对象集合不存 在,会创建一个这样的集合。如果集合当前没有闲置的对象可以返回,并且当前的active < _maxActive && totalActive + _totalIdle < _maxTotal,那么通过KeyedPoolableObjectFactory创建一个新的对象;否则就根据 _whenExhaustedAction的设定执行相应的操作。要注意每次都会执行 _factory.activateObject(key,pair.value)方法初始化这个对象。在返回这个对象之前,如果设定了 _testOnBorrow标志则调用_factory.validateObject(Object key, Object obj)方法,如果不符合要求会抛出NoSuchElementException异常,否则返回这个对象。
4、returnObject(Object key, Object obj)
这个方法用来把一个对象返回给key表示的对象集合,这个 方法的执行过程与borrowObject(Object key)方法相似。它会根据_testOnReturn标志执行_factory.validateObject(Object key, Object obj)方法检查返回的对象是否合格。要注意每次都会执行 _factory.passivateObject(key, obj)方法“消除”这个对象。
5、过期类的回收:
Evictor 为一个内嵌类,实现自Runnable接口。它每过_timeBetweenEvictionRunsMillis毫秒会调用一次evict()方法,在 后台收集垃圾寿命超过_minEvictableIdleTimeMillis毫秒的对象。每当我们创建一个 GenericKeyedObjectPool对象的时候,这个类会被自动创建,并且被启动。evict() 方法很简单,它首先创建一个_evictionKeyCursor = _poolList.cursor()对象,然后根据key遍历每一个对象集合。
查 找到过期的就会删除。
三、SoftReferenceObjectPool
通过SoftReference进行对象的保存,这个SoftReference的 好处是你不需要自己进行收集,它会在JVM的内存用尽时被自动回收。这种对象池的特色是:
a、可以保存任意多个对象,不会有容量已满的情况发 生。
b、在对象池已空的时候,调用它的borrowObject方法,会自动返回新创建的实例。
c、可以在初始化同时,在池内预先创建一定量的对象。
d、当内存不足的时候,池中的对象可以被Java虚拟机回收。
四、StackObjectPool 和 StackKeyedObjectPool
通过一个Stack对对象进行管理,要注意 这个Stack是继承自Vector对象的。因此虽然线程安全,但是效率较低。这种对象池的特色是:
a、可以为对象池指定一个初始的参考大小 (当空间不够时会自动增长)。
b、在对象池已空的时候,调用它的borrowObject方法,会自动返回新创建的实例。
c、可以为对象池指定一个可保存的对象数目的上限。达到这个上限之后,再向池里送回的对象会被自动送去回收。
五、PoolableObjectFactory
Pool组件利用PoolableObjectFactory来照看被池化的对象。 ObjectPool的实例在需要处理被池化的对象的产生、激活、挂起、校验和销毁工作时,就会调用跟它关联在一起的 PoolableObjectFactory实例的相应方法来操作。 用户需要自己实现这个接口。
相关推荐
本文将深入解析Struts1的源码,以帮助理解其内部工作原理。 首先,我们从ActionServlet的生命周期开始。ActionServlet是Struts1的核心组件,它的生命周期分为初始化、拦截请求和销毁三个阶段。在初始化阶段,`init...
STRUTS2源码解析STRUTS2源码解析STRUTS2源码解析STRUTS2源码解析STRUTS2源码解析STRUTS2源码解析STRUTS2源码解析STRUTS2源码解析STRUTS2源码解析STRUTS2源码解析STRUTS2源码解析STRUTS2源码解析STRUTS2源码解析...
在"struts2源码解析.pdf"文档中,主要探讨了以下几个关键组件及其功能: 1. **ActionContext**: - `ActionContext`是Struts2的核心上下文,它存储了与当前Action执行相关的所有信息,如请求参数、session数据等。...
配置相关的类位于`org.apache.struts2.config`包,这里包含读取和解析XML及properties文件的类。`org.apache.struts2.interceptor`包定义了内置的拦截器,例如身份验证、异常处理等,开发者可以根据需要自定义拦截器...
### Struts2源码解析及工作原理 #### Struts2简介 Struts2是一个流行的Java Web应用程序框架,它继承和发展了Struts1.x的一些特性,同时又采用了WebWork框架的核心技术,使得Struts2在设计理念和技术实现上都有了...
学习Struts1源码有助于开发者更好地理解Web应用的架构设计,以及MVC模式的实现细节,对于提升Java Web开发技能和解决问题具有很大帮助。同时,虽然Struts1已逐渐被更新的框架如Struts2和Spring MVC取代,但其设计...
struts2源码详细解析51CTO下载-struts2源代码分析(个人觉得非常经典)
Struts2是一个基于MVC设计模式的Web应用框架,它本质上相当于一个servlet,在MVC设计模式中,Struts2作为控制器(Controller)来建立模型与视图的数据交互。Struts 2是Struts的下...文档中对于代码进行重要部分的解析。
这份"struts2源码解析[归纳].pdf"文档显然深入探讨了Struts2的核心组件和工作原理。以下是对其中提到的关键概念的详细解释: 1. **ActionContext**: ActionContext是Struts2中一个非常重要的类,它封装了当前请求...
Struts2 源码分析 Struts2 是一个基于MVC 模式的Web 应用程序框架,它的源码分析可以帮助我们更好地理解框架的内部机制和工作流程。下面是Struts2 源码分析的相关知识点: 1. Struts2 架构图 Struts2 的架构图...
总的来说,这篇“Struts2源码解读”的博文应该是对Struts2核心机制进行了详细的解析,包括Action、Interceptor、Result等关键组件的工作原理,以及整个请求处理流程。通过学习这些内容,开发者可以深化对Struts2的...
4. `ConfigurationProvider`和`Configuration`:`ConfigurationProvider`解析Struts2的配置文件,如`struts.xml`。默认实现`XmlConfigurationProvider`和`StrutsXmlConfigurationProvider`负责读取和解析这些配置。 ...
1. **Struts2的核心概念** - **Action**: Action是业务逻辑的载体,负责处理用户请求并返回结果。在Struts2中,Action可以通过实现`com.opensymphony.xwork2.Action`接口或继承`org.struts2.interceptor栈...
本资料包包含的是《Struts2深入详解》一书的源码分析,涵盖了从第一章到第五章的内容,并附带了相关的jar包,方便读者结合理论与实践进行学习。 首先,让我们从第一章开始,Struts2的基础知识。这一章通常会介绍...
Struts2的源码解析文档通常会涵盖以下几个核心部分: 1. **FilterDispatcher**:这是Struts2框架的入口点,负责拦截HTTP请求并根据配置将请求转发到相应的Action。源码分析会解释其如何处理请求、如何查找Action及...
Struts2的源码解析有助于深入理解其工作原理,这对于开发者优化性能、调试问题以及自定义扩展功能至关重要。 在Struts2.0源码中,以下几个核心组件和概念值得深入探讨: 1. **Action类**:Action是业务逻辑的载体...
Struts 1.2 是一款基于 Model-View-Controller(MVC)设计模式的开源Java Web框架,由Apache...通过深入理解Struts 1.2的源码,开发者可以更好地利用这个框架,解决实际项目中的问题,同时为自定义扩展和优化提供可能。
在`Dispatcher`的初始化过程中,它会读取`web.xml`或其他配置文件,解析Struts2的配置信息,如Action、结果类型、拦截器等。这些配置信息被加载到内存中的容器中,供后续请求处理时使用。`Dispatcher`还负责初始化...
这里的"struts-1.3.9 源码"指的是Struts 1.x系列的第9次次要版本的源代码。Struts 1.3.9是在2008年发布的,它提供了许多增强和修复了之前版本中的问题,以提高框架的稳定性和安全性。 首先,我们来看看`LICENSE.txt...
下面我们将深入探讨Struts 1的源码分析,特别是针对Struts 1.2版本。 1. **架构概述** - **DispatcherServlet**:作为Struts的核心,DispatcherServlet负责接收HTTP请求并分发到相应的Action。 - **ActionMapping...