struts的执行主要靠了一个ActionServlet进行控制,它是中央控制器,一个叫RequestProcessor的类非常重要,public class ModuleConfigImpl implements Serializable, ModuleConfig 这个类也非常重要,主要就是用它来读取配置文件信息的,struts的主要处理流程都是在这个类里面,分析源码,先要弄清楚几个重要的类所对应的东西,即每个类都会对应着配置文件中的一些信息
ActionMapping对应着struts-config.xml中的一条action标签的配置信息,ExceptionConfig对应着一条异常配置,FormBeanConfig也是对应着一条ActionForm的配置
首先到RequestProcessor的process里面去看,主要流程就在这个方法里面
// Wrap multipart requests with a special wrapper
request = processMultipart(request);//这句代码的作用是包装从servlet传过来的request,是用作上传的
第二步:
// Identify the path component we will use to select a mapping
String path = processPath(request, response);//它是用来截取这次请求的path路径的
第三步:
处理国际化
// Select a Locale for the current user if requested
processLocale(request, response);代码实现如下:
protected void processLocale(HttpServletRequest request,
HttpServletResponse response) {
// Are we configured to select the Locale automatically?
if (!moduleConfig.getControllerConfig().getLocale()) {
return;
}
// Has a Locale already been selected?
HttpSession session = request.getSession();
if (session.getAttribute(Globals.LOCALE_KEY) != null) {//如果是你已经访问过了,并且还没有关浏览器,session还没有过期的情况下,是不为null的
return;
}
// Use the Locale returned by the servlet container (if any)
Locale locale = request.getLocale();
if (locale != null) {
if (log.isDebugEnabled()) {
log.debug(" Setting user locale '" + locale + "'");
}
session.setAttribute(Globals.LOCALE_KEY, locale);//第一次访问的时候是没有Local信息的,那么从request里面取得并设置到session里面去
}
}
第四步:处理contentType和cache
processContent(request, response);
processNoCache(request, response);
注意一下:
protected void processNoCache(HttpServletRequest request,
HttpServletResponse response) {
if (moduleConfig.getControllerConfig().getNocache()) {//如果在配置中没有设置不缓存的话,这个是为假的,不会进行处理
response.setHeader("Pragma", "No-cache");
response.setHeader("Cache-Control", "no-cache,no-store,max-age=0");
response.setDateHeader("Expires", 1);
}
}
第五步:清理缓存中的异常消息等,如果没有设置的话当然是没有的
this.processCachedMessages(request, response);
第六步:得到这次请求的一个mapping(重要)
ActionMapping mapping = processMapping(request, response, path);
protected ActionMapping processMapping(HttpServletRequest request,
HttpServletResponse response,
String path)
throws IOException {
// Is there a mapping for this path?
ActionMapping mapping = (ActionMapping)
moduleConfig.findActionConfig(path);
// If a mapping is found, put it in the request and return it
if (mapping != null) {
request.setAttribute(Globals.MAPPING_KEY, mapping);//将mapping设置到request里面去
return (mapping);
}
moduleConfig.findActionConfig(path);方法如下:这个moduleConfig是专门用来读取struts-config.xml文件的,当tomcat一启动的时候那么就已经把配置读取到内存里了,并把东西放到一个map里面,如下:
Map map = new HashMap();
map.put("/servlet/delUser", actionMapping);
map.put("/servlet/addUser", actionMapping);
map.put("/servlet/modifyUser", actionMapping);
map.put("/servlet/queryUser", actionMapping);
public ActionConfig findActionConfig(String path) {
ActionConfig config = (ActionConfig) actionConfigs.get(path);//actionConfigs就是个map,此时读取到的肯定是不为null的
// If a direct match cannot be found, try to match action configs
// containing wildcard patterns
if (config == null) {
config = matcher.match(path);
}
return config;
}
第七步:处理ActionForm
ActionForm form = processActionForm(request, response, mapping);
方法定义如下:
protected ActionForm processActionForm(HttpServletRequest request,
HttpServletResponse response,
ActionMapping mapping) {
// Create (if necessary) a form bean to use
ActionForm instance = RequestUtils.createActionForm//方法声明在后面
(request, mapping, moduleConfig, servlet);
if (instance == null) {
return (null);
}
// Store the new instance in the appropriate scope
if (log.isDebugEnabled()) {
log.debug(" Storing ActionForm bean instance in scope '" +
mapping.getScope() + "' under attribute key '" +
mapping.getAttribute() + "'");
}
if ("request".equals(mapping.getScope())) {
request.setAttribute(mapping.getAttribute(), instance);
} else {
HttpSession session = request.getSession();
session.setAttribute(mapping.getAttribute(), instance);
}//从这两句可以看出来在页面中可以通过相应的表达式可以把ActionForm里面的东西取出来
return (instance);
}
RequestUtils.createActionForm方法如下:
public static ActionForm createActionForm(
HttpServletRequest request,
ActionMapping mapping,
ModuleConfig moduleConfig,
ActionServlet servlet) {
// Is there a form bean associated with this mapping?
String attribute = mapping.getAttribute();//方法如后面
if (attribute == null) {
return (null);
}
// Look up the form bean configuration information to use
String name = mapping.getName();
FormBeanConfig config = moduleConfig.findFormBeanConfig(name);//里面也是这样return ((FormBeanConfig) formBeans.get(name));也是用了map来存储信息的
if (config == null) {
log.warn("No FormBeanConfig found under '" + name + "'");
return (null);
}
ActionForm instance = lookupActionForm(request, attribute, mapping.getScope());//方法定义如后面
// Can we recycle the existing form bean instance (if there is one)?
try {
if (instance != null && canReuseActionForm(instance, config)) {
return (instance);
}
} catch(ClassNotFoundException e) {
log.error(servlet.getInternal().getMessage("formBean", config.getType()), e);
return (null);
}
return createActionForm(config, servlet);//如果scope是request的话,那么会用这个方法去得到ActionForm,他是采用反射的方式来生成的
}
mapping的getAttribute()方法如下:如果没有指定attribute属性,那么会用name属性的值
public String getAttribute() {
if (this.attribute == null) {
return (this.name);
} else {
return (this.attribute);
}
}
lookupActionForm(request, attribute, mapping.getScope());方法如下:
private static ActionForm lookupActionForm(HttpServletRequest request, String attribute, String scope)
{
// Look up any existing form bean instance
if (log.isDebugEnabled()) {
log.debug(
" Looking for ActionForm bean instance in scope '"
+ scope
+ "' under attribute key '"
+ attribute
+ "'");
}
ActionForm instance = null;
HttpSession session = null;
if ("request".equals(scope)) {
instance = (ActionForm) request.getAttribute(attribute);//先到request去找,如果scope设置为request,当然这次是找不到的,所以会返回null
} else {
session = request.getSession();
instance = (ActionForm) session.getAttribute(attribute);
}
return (instance);
}
第八步:processPopulate(request, response, form, mapping);//收集表单的数据工作
首先执行ActionForm中的reset方法进行重置,然后得到表单中所有输入域的name名称,再调用request.getParameterValues(),根据name名称得到相应的值,最后将表单中的数据全部放到一个map中,map的key为表单输入域的名称,map的value为表单输入域的值(字符串数组),接下来调用一个第三方组件BeanUtils,将Map中的值,根据ActionForm中的类型先转换好,再调用ActionForm中的setter方法设置到ActionForm上
搜集过程是这样的:
* 将页面数据放到map中,其中map中的key为页面中的名称,map中的value为页面中的value值
* 调用BeanUtils.setProperties方法,将map中的值逐个设置到ActionForm实例上,对于ActionForm中的每个属性 根据类型调用相应的Converter,然后调用相应的convert方法,将相应的字符串转换成ActionForm中指定的类型
第九步:取得Action
Action action = processActionCreate(request, response, mapping);
protected Action processActionCreate(HttpServletRequest request,
HttpServletResponse response,
ActionMapping mapping)
throws IOException {
// Acquire the Action instance we will be using (if there is one)
String className = mapping.getType();
if (log.isDebugEnabled()) {
log.debug(" Looking for Action instance for class " + className);
}
// :TODO: If there were a mapping property indicating whether
// an Action were a singleton or not ([true]),
// could we just instantiate and return a new instance here?
Action instance = null;
synchronized (actions) {
// Return any existing Action instance of this class
instance = (Action) actions.get(className);//当第一次进来的时候是没有的,从这里可以看出来Action是单实例多线程的
if (instance != null) {
if (log.isTraceEnabled()) {
log.trace(" Returning existing Action instance");
}
return (instance);
}
// Create and return a new Action instance
if (log.isTraceEnabled()) {
log.trace(" Creating new Action instance");
}
try {
instance = (Action) RequestUtils.applicationInstance(className);
// :TODO: Maybe we should propagate this exception
// instead of returning null.
} catch (Exception e) {
log.error(
getInternal().getMessage("actionCreate", mapping.getPath()),
e);
response.sendError(
HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
getInternal().getMessage("actionCreate", mapping.getPath()));
return (null);
}
instance.setServlet(this.servlet);
actions.put(className, instance);
}
return (instance);
}
第十步:处理我们自己的Action并返回ActionForward
ActionForward forward = processActionPerform(request, response,action, form, mapping);
第十一步:完成转向:
processForwardConfig(request, response, forward);
protected void processForwardConfig(HttpServletRequest request,
HttpServletResponse response,
ForwardConfig forward)
throws IOException, ServletException {
if (forward == null) {
return;
}
if (log.isDebugEnabled()) {
log.debug("processForwardConfig(" + forward + ")");
}
String forwardPath = forward.getPath();
String uri = null;
// paths not starting with / should be passed through without any processing
// (ie. they're absolute)
if (forwardPath.startsWith("/")) {
uri = RequestUtils.forwardURL(request, forward, null); // get module relative uri
} else {
uri = forwardPath;
}
if (forward.getRedirect()) {//如果forward中配置了redirect,则会用重定向进行转向,否则采用转发
// only prepend context path for relative uri
if (uri.startsWith("/")) {
uri = request.getContextPath() + uri;
}
response.sendRedirect(response.encodeRedirectURL(uri));
} else {
doForward(uri, request, response);
}
}