struts执行流程

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);
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

yjsuge

你的鼓励是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值