几个重要类的说明
ActionServlet struts的核心类,用于初始化struts配置文件,处理发送到action的请求;ModuleConfig struts配置文件信息加载的接口,ModuleConfigImpl是接口的默认实现,可以完成基于配置信息的信息读、取操作。每个struts模块文件对应一个实例。
ActionConfig 用于存储action配置信息的实体,配置文件中每一个action对应一个ActionConfig。该类包含该action所在struts配置文件对应的ModuleConfig;它的子类ActionMapping很眼熟吧,action层方法的参数中都有它。
RequestProcessor 请求处理实体类负责处理Action拦截到的请求处理。
初始化加载
struts的启动加载类是ActionServlet,文如其名它是Servlet的扩展,因此初始化在init方法中执行。public void init() throws ServletException {
// Wraps the entire initialization in a try/catch to better 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) 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) {
// The follow error message is not retrieved from internal message
// resources as they may not have been able to have been
// initialized
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());
}
}
该方法中initModuleConfig是配置信息的加载方法,它没掉用了两次:
//用于加载struts必须的配置struts-config.xml
ModuleConfig moduleConfig = initModuleConfig("", config);
//用于加载配其他struts配置文件
moduleConfig = initModuleConfig
(prefix, getServletConfig().getInitParameter(name));
从以上代码可以看出,不同的struts配置文件肯定是根据参数进行区分了,其中prefix是web.xml中struts配置信息去掉“config/”之后param-name,下面配置信息中的prefix为page/user
<!-- 用户管理配置文件 -->
<init-param>
<param-name>config/page/user</param-name>
<param-value>
/WEB-INF/config/struts/module/struts-user.xml
</param-value>
</init-param>
如何区分和保存,看下面的代码:
protected ModuleConfig initModuleConfig(String prefix, 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 = paths.trim();
paths = "";
}
if (path.length() < 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;
}
代码中,ModuleConfig对象是通过ModuleConfig config = factoryObject.createModuleConfig(prefix);创建的,继续跟进
/**
* A factory for creating {@link ModuleConfig} instances.
*
* @see ModuleConfig
* @see ModuleConfigFactory
*
* @version $Rev: 54929 $ $Date: 2004-10-16 17:38:42 +0100 (Sat, 16 Oct 2004) $
*/
public class DefaultModuleConfigFactory extends ModuleConfigFactory implements Serializable{
// --------------------------------------------------------- Public Methods
/**
* Create and return a newly instansiated {@link ModuleConfig}.
* This method must be implemented by concrete subclasses.
*
* @param prefix Module prefix for Configuration
*/
public ModuleConfig createModuleConfig(String prefix) {
return new ModuleConfigImpl(prefix);
}
}
/**
* Construct an ModuleConfigImpl object according to the specified
* parameter values.
*
* @param prefix Context-relative URI prefix for this module
*/
public ModuleConfigImpl(String prefix) {
super();
this.prefix = prefix;
this.actionConfigs = new HashMap();
this.actionConfigList = new ArrayList();
this.actionFormBeanClass = "org.apache.struts.action.ActionFormBean";
this.actionMappingClass = "org.apache.struts.action.ActionMapping";
this.actionForwardClass = "org.apache.struts.action.ActionForward";
this.configured = false;
this.controllerConfig = null;
this.dataSources = new HashMap();
this.exceptions = new HashMap();
this.formBeans = new HashMap();
this.forwards = new HashMap();
this.messageResources = new HashMap();
this.plugIns = new ArrayList();
}
清晰明了,struts为每一个配置文件创建了一个ModuleConfigImpl对象,通过该对象的prefix属性区分不同的配置文件。
额外的细节,initModuleConfig中还做了两件事,第一,配置信息使用common-digester读取配置信息并放入ModuleConfigImpl对象中(可以阅读Digester digester = initConfigDigester();和this.parseModuleConfigFile(digester, path);这两个方法);第二,struts将加载好的ModuleConfigImpl放入了ServletContext中(getServletContext().setAttribute(Globals.MODULE_KEY + config.getPrefix(),config);)。
异常处理
action层抛出异常后,被ActionServlet捕获,processException完成了异常信息的处理
/**
* <P>Ask the specified <code>Action</code> instance to handle this
* request. Return the <code>ActionForward</code> instance (if any)
* returned by the called <code>Action</code> for further processing.
* </P>
*
* @param request The servlet request we are processing
* @param response The servlet response we are creating
* @param action The Action instance to be used
* @param form The ActionForm instance to pass to this Action
* @param mapping The ActionMapping instance to pass to this Action
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet exception occurs
*/
protected ActionForward
processActionPerform(HttpServletRequest request,
HttpServletResponse response,
Action action,
ActionForm form,
ActionMapping mapping)
throws IOException, ServletException {
try {
return (action.execute(mapping, form, request, response));
} catch (Exception e) {
return (processException(request, response,
e, form, mapping));
}
}
/**
* <p>Ask our exception handler to handle the exception. Return the
* <code>ActionForward</code> instance (if any) returned by the
* called <code>ExceptionHandler</code>.</p>
*
* @param request The servlet request we are processing
* @param response The servlet response we are processing
* @param exception The exception being handled
* @param form The ActionForm we are processing
* @param mapping The ActionMapping we are using
*
* @exception IOException if an input/output error occurs
* @exception ServletException if a servlet exception occurs
*/
protected ActionForward processException(HttpServletRequest request,
HttpServletResponse response,
Exception exception,
ActionForm form,
ActionMapping mapping)
throws IOException, ServletException {
// Is there a defined handler for this exception?
ExceptionConfig config = mapping.findException(exception.getClass());
if (config == null) {
log.warn(getInternal().getMessage("unhandledException",
exception.getClass()));
if (exception instanceof IOException) {
throw (IOException) exception;
} else if (exception instanceof ServletException) {
throw (ServletException) exception;
} else {
throw new ServletException(exception);
}
}
// Use the configured exception handling
try {
ExceptionHandler handler = (ExceptionHandler)
RequestUtils.applicationInstance(config.getHandler());
return (handler.execute(exception, config, mapping, form,
request, response));
} catch (Exception e) {
throw new ServletException(e);
}
}
processException干了两件事,首先获取异常配置信息(ExceptionConfig config = mapping.findException(exception.getClass());),若获取不到且不是IO或Servlet的异常,抛出ServletException让容器处理;若读取到异常配置信息,则根据配置信息尝试用ExceptionHandler处理异常。
获取异常配置信息的流程中,还有一些东西,mapping是struts配置文件中某个action对应的配置信息
/**
* <p>Find and return the <code>ExceptionConfig</code> instance defining
* how <code>Exceptions</code> of the specified type should be handled.
* This is performed by checking local and then global configurations for
* the specified exception's class, and then looking up the superclass chain
* (again checking local and then global configurations). If no handler
* configuration can be found, return <code>null</code>.</p>
*
* <p>Introduced in <code>ActionMapping</code> in Struts 1.1, but pushed
* up to <code>ActionConfig</code> in Struts 1.2.0.</p>
*
* @param type Exception class for which to find a handler
* @since Struts 1.2.0
*/
public ExceptionConfig findException(Class type) {
// Check through the entire superclass hierarchy as needed
ExceptionConfig config = null;
while (true) {
// Check for a locally defined handler
String name = type.getName();
config = findExceptionConfig(name);
if (config != null) {
return (config);
}
// Check for a globally defined handler
config = getModuleConfig().findExceptionConfig(name);
if (config != null) {
return (config);
}
// Loop again for our superclass (if any)
type = type.getSuperclass();
if (type == null) {
break;
}
}
return (null); // No handler has been configured
}
首先他会尝试根据异常Class信息,读取当前action的异常处理配置,若没有读取到,则通过getModuleConfig()获取当前action所在struts配置文件的信息(config = getModuleConfig().findExceptionConfig(name);),从而尝试得到全局异常(global-exceptions)配置。
如果当前Class没有获取到,该方法会查找当前异常类的父类,并查看action和struts配置的异常处理配置中是否有相应配置。
知识点总结
1. struts对没一个struts配置文件都生成一个ModuleConfigImpl对象存放其信息;每一个struts配置文件汇总的action配置都会生成一个ActionConfig对象存放其配置信息;
2. ActionServlet可以捕获到Action层抛出的异常信息,但是真正的处理是依靠RequestProcesser完成的(processException方法);
3. struts的异常配置首先读取当前action的,然后是struts配置中全局的;
4. struts的异常可以向上查询,比如struts会一次查找IllegalArgumentException-》RuntimeException-》Exception-》Throwable。