struts1.3执行流分析

来源:互联网 发布:数据分析基础知识 编辑:程序博客网 时间:2024/05/31 18:31

这是在去年9月份,读了一下struts1.3的源码,记录了一下执行流程。发出来和大家分享一下吧。这个流程还是很经典的吧。有空再读读struts2的^_^ 

执行流程:

1、ActionServlet处理.do的请求 不管是get还是post方式都将转到 
    protected void process(HttpServletRequest request, HttpServletResponse response) 方法。 
     
2、根据请求对象和servletContext对象选择请求所隶属的模块 
    ModuleUtils.getInstance().selectModule(request, getServletContext()); 
     
3、加载模块配置对象 ModuleConfig config = getModuleConfig(request); 

4、加载请求处理对象 
    RequestProcessor processor = getProcessorForModule(config); 
        if (processor == null) { 
            processor = getRequestProcessor(config); 
        } 
         
5、调用请求对象(processor)对象的 
    public void process(HttpServletRequest request, HttpServletResponse response) 
        throws IOException, ServletException 
    方法处理请求。 
     
6、对mutipart请求(上传)进行特殊包装 request = processMultipart(request); 

    1、首先判断是否为post方式,如果不是post方式,则肯定不是上传请求,则直接返回request对象 
        if (!"POST".equalsIgnoreCase(request.getMethod())) { 
            return (request); 
        } 
         
    2、获取request对象的ContentType,如果ContentType为multipart/form-datade 话则 new 一个 MultipartRequestWrapper 对象返回。否则直接返回request。 
        String contentType = request.getContentType(); 
        if ((contentType != null) 
            && contentType.startsWith("multipart/form-data")) { 
            return (new MultipartRequestWrapper(request)); 
        } else { 
            return (request); 
        } 
         
        1、MultipartRequestWrapper继承于HttpServletRequestWrapper。下面是包装代码 
            public MultipartRequestWrapper(HttpServletRequest request) { 
                super(request); 
                this.parameters = new HashMap(); 
            } 
             
7、处理请求路径 
    String path = processPath(request, response); 返回的是访问的action的名字 
     
8、如果返回值是空, 则方法直接return,结束。 
    if (path == null) { 
        return; 
    } 
     
9、把请求的方式(post/get)和action名字记入日志 
    if (log.isDebugEnabled()) { 
        log.debug("Processing a '" + request.getMethod() + "' for path '" 
            + path + "'"); 
    } 
     
10、为当前的用户请求选择对应的local(区域和语言),这是根据浏览器的设置的。涉及到国际化问题。 
    // Select a Locale for the current user if requested 
    processLocale(request, response); 

11、为response对象设置ContentType和no-cache的header信息。 
    // Set the content type and no-caching headers if requested 
    processContent(request, response); 
    processNoCache(request, response); 

12、留了一个可以预处理请求的扩展接口。 
    // General purpose preprocessing hook 
    if (!processPreprocess(request, response)) { 
        return; 
    }      这里processPreprocess方法只有一句话:return(true);其实是为了可以扩展,如果要对请求预处理,可以继承这个类,然后重写这个 
    protected boolean processPreprocess(HttpServletRequest request,HttpServletResponse response) { 
        return (true); 
    } 
方法。 

13、处理以前缓存的信息 
    this.processCachedMessages(request, response); 
    其实就是清空session里如果存在的struts定义的提示信息和错误信息。     

14、根据request,response,和path(action的名字)返回actionMapping对象。 
    // Identify the mapping for this request 
    ActionMapping mapping = processMapping(request, response, path); 
    if (mapping == null) { 
        return; 
    } 
     
    1、首先去配置文件里找相应的配置信息 
        // Is there a mapping for this path? 
        ActionMapping mapping = (ActionMapping) moduleConfig.findActionConfig(path); 
     
    2、如果有配置则把它放入request,并返回他。 
        // If a mapping is found, put it in the request and return it 
        if (mapping != null) { 
            request.setAttribute(Globals.MAPPING_KEY, mapping); 
            return (mapping); 
        } 
         
    3、找到“未知的映射路径(如果有的话)”。同样找到了就放到request里并返回他。 
        // Locate the mapping for unknown paths (if any) 
        ActionConfig[] configs = moduleConfig.findActionConfigs(); 

        for (int i = 0; i < configs.length; i++) { 
            if (configs[i].getUnknown()) { 
                mapping = (ActionMapping) configs[i]; 
                request.setAttribute(Globals.MAPPING_KEY, mapping); 

                return (mapping); 
            } 
        } 
     
    4、如果还是没有找到mapping信息则发送错误消息,并返回null 
        // No mapping can be found to process this request 
        String msg = getInternal().getMessage("processInvalid"); 

        log.error(msg + " " + path); 
        response.sendError(HttpServletResponse.SC_NOT_FOUND, msg); 

        return null; 

15、检查执行这个action所要的所有角色(是否有权访问) 
    // Check for any role required to perform this action 
    if (!processRoles(request, response, mapping)) { 
        return; 
    } 
     
    1、从actionMapping(mapping)对想里取得角色名称的数组。 
        // Is this action protected by role requirements? 
        String[] roles = mapping.getRoleNames(); 
         
    2、如果mapping里没有角色信息(没有配置),就不做处理,直接返回true 
        if ((roles == null) || (roles.length < 1)) { 
            return (true); 
        } 
         
    3、依次取出配置了的角色 ,如果用户在角色中 (配置了的所有角色中的任意一个) ,则把用户名和角色名记 录到log里。并返回true。 
        // Check the current user against the list of required roles 
        for (int i = 0; i < roles.length; i++) { 
            if (request.isUserInRole(roles[i])) { 
                if (log.isDebugEnabled()) { 
                    log.debug(" User '" + request.getRemoteUser() 
                        + "' has role '" + roles[i] + "', granting access"); 
                } 

                return (true); 
            } 
        } 
         
    4、如果仍没找到用户所对应的角色,则说明这个用户是非法访问的。则把这个用户名记录到log里,发送错误信息,并返回false。 
        // The current user is not authorized for this action 
        if (log.isDebugEnabled()) { 
            log.debug(" User '" + request.getRemoteUser() 
                + "' does not have any required role, denying access"); 
        } 

        response.sendError(HttpServletResponse.SC_FORBIDDEN, 
            getInternal().getMessage("notAuthorized", mapping.getPath())); 

        return (false); 

16、处理与这个请求有关的所有actionForm。(调用processActionForm()方法返回ActionForm对象) 
    // Process any ActionForm bean related to this request 
    ActionForm form = processActionForm(request, response, mapping); 
     
    1、如果有需要就新建一个ActionForm来供使用。 
        // Create (if necessary) a form bean to use 
        ActionForm instance = RequestUtils.createActionForm(request, mapping, moduleConfig, servlet); 
         
        1、查看mapping里是否配置name属性或attribute属性来指定ActionForm,如果都没有则返回null 
            // Is there a form bean associated with this mapping? 
            String attribute = mapping.getAttribute(); 

            if (attribute == null) { 
                return (null); 
            } 
         
        2、通过name属性拿到ActionForm的配置信息 
            // Look up the form bean configuration information to use 
            String name = mapping.getName(); 
            FormBeanConfig config = moduleConfig.findFormBeanConfig(name); 
         
        3、如果没有与name属性相对应的<form-bean>配置,则在log里记录:没有配置与name对应的formBean,并返回null; 
            if (config == null) { 
                log.warn("No FormBeanConfig found under '" + name + "'"); 

                return (null); 
            } 
             
        4、根据拿到的<form-bean>配置,在相应的范围里(request,session)找ActionForm的实例 
            ActionForm instance = lookupActionForm(request, attribute, mapping.getScope()); 
         
        5、如果找到,并被判定为可用,则返回找到的实例。 
            // Can we recycle the existing form bean instance (if there is one)? 
            if ((instance != null) && config.canReuse(instance)) { 
                return (instance); 
            } 
         
        6、如果没找到,(前面已经确定配置了formBean)。则新建一个ActionForm对象出来并返回他。 
            return createActionForm(config, servlet); 
             
            1、首先判断传入的config,如果config为null,则直接返回null 
                if (config == null) { 
                    return (null); 
                } 
             
            2、创建并返回一个新的ActionForm对象。这里调用了config对象的createActionForm方法。该方法里肯定用到了反射机制。另外把创建的ActionForm或动态ActionForm的信息存到log里。同样,如果过程中出错,错误信息业将被保存到日志里。 
                ActionForm instance = null; 

                // Create and return a new form bean instance 
                try { 
                    instance = config.createActionForm(servlet); 

                    if (log.isDebugEnabled()) { 
                        log.debug(" Creating new " 
                            + (config.getDynamic() ? "DynaActionForm" : "ActionForm") 
                            + " instance of type '" + config.getType() + "'"); 
                        log.trace(" --> " + instance); 
                    } 
                } catch (Throwable t) { 
                    log.error(servlet.getInternal().getMessage("formBean", 
                            config.getType()), t); 
                } 

                return (instance); 
                 
17、为ActionForm填充数据。 
    processPopulate(request, response, form, mapping); 
     
    1、首先判断form是否为null,如果是则直接return。 
        if (form == null) { 
            return; 
        } 
     
    2、往log里写入一句话提示从这里开始填充formBean 
        if (log.isDebugEnabled()) { 
            log.debug(" Populating bean properties from this request"); 
        } 
     
    3、设置Servlet。 
        form.setServlet(this.servlet); 
        (不知道具体作用) 
         
    4、执行reset方法重置表单。默认reset方法不做任何事情。这个方法是为了方便扩展。可以继承ActionForm类重写reset方法,这个方法可以用来做设置一些默认值等工作。 
        form.reset(mapping, request); 
         
    5、如果是上传表单,则获取上传类。(不甚了解) 
        if (mapping.getMultipartClass() != null) { 
            request.setAttribute(Globals.MULTIPART_KEY, 
                mapping.getMultipartClass()); 
        } 
     
    6、填充form 
        RequestUtils.populate(form, mapping.getPrefix(), mapping.getSuffix(), request); 
         
        1、建立一个HashMap 用于存放属性 
            // Build a list of relevant request parameters from this request 
            HashMap properties = new HashMap(); 
             
        2、建立一个Enumeration用于存放参数名 
            // Iterator of parameter names 
            Enumeration names = null; 
         
        3、建立一个Map来存放multipart参数 
            // Map for multipart parameters 
            Map multipartParameters = null; 
         
        4、获取请求的ContentType和Method。并设置multipart表示为false。 
            String contentType = request.getContentType(); 
            String method = request.getMethod(); 
            boolean isMultipart = false; 
             
        5、如果是multipart表单则做上传处理(不甚了解) 
            if (bean instanceof ActionForm) { 
                ((ActionForm) bean).setMultipartRequestHandler(null); 
            } 

            MultipartRequestHandler multipartHandler = null; 
            if ((contentType != null) 
                && (contentType.startsWith("multipart/form-data")) 
                && (method.equalsIgnoreCase("POST"))) { 
                // Get the ActionServletWrapper from the form bean 
                ActionServletWrapper servlet; 

                if (bean instanceof ActionForm) { 
                    servlet = ((ActionForm) bean).getServletWrapper(); 
                } else { 
                    throw new ServletException("bean that's supposed to be " 
                        + "populated from a multipart request is not of type " 
                        + "\"org.apache.struts.action.ActionForm\", but type " 
                        + "\"" + bean.getClass().getName() + "\""); 
                } 

                // Obtain a MultipartRequestHandler 
                multipartHandler = getMultipartHandler(request); 

                if (multipartHandler != null) { 
                    isMultipart = true; 

                    // Set servlet and mapping info 
                    servlet.setServletFor(multipartHandler); 
                    multipartHandler.setMapping((ActionMapping) request 
                        .getAttribute(Globals.MAPPING_KEY)); 

                    // Initialize multipart request class handler 
                    multipartHandler.handleRequest(request); 

                    //stop here if the maximum length has been exceeded 
                    Boolean maxLengthExceeded = 
                        (Boolean) request.getAttribute(MultipartRequestHandler.ATTRIBUTE_MAX_LENGTH_EXCEEDED); 

                    if ((maxLengthExceeded != null) 
                        && (maxLengthExceeded.booleanValue())) { 
                        ((ActionForm) bean).setMultipartRequestHandler(multipartHandler); 
                        return; 
                    } 

                    //retrieve form values and put into properties 
                    multipartParameters = 
                        getAllParametersForMultipartRequest(request, 
                            multipartHandler); 
                    names = Collections.enumeration(multipartParameters.keySet()); 
                } 
            } 
             
        6、如果不是上传,则把参数名存到names枚举里面。 
            if (!isMultipart) { 
                names = request.getParameterNames(); 
            } 
         
        7、遍历这个枚举 
            while (names.hasMoreElements()) 
             
            1、把名字拿出来存到name和stripped变量里 
                String name = (String) names.nextElement(); 
                String stripped = name; 
                 
            2、去掉name的前缀和后缀(如果有的话(配置文件里可以配置)) 
                if (prefix != null) { 
                    if (!stripped.startsWith(prefix)) { 
                        continue; 
                    } 

                    stripped = stripped.substring(prefix.length()); 
                } 

                if (suffix != null) { 
                    if (!stripped.endsWith(suffix)) { 
                        continue; 
                    } 

                    stripped = 
                        stripped.substring(0, stripped.length() - suffix.length()); 
                } 
                 
            3、获取参数值,分上传和非上传两种方式 
                Object parameterValue = null; 

                if (isMultipart) { 
                    parameterValue = multipartParameters.get(name); 
                    parameterValue = rationalizeMultipleFileProperty(bean, name, parameterValue); 
                } else { 
                    parameterValue = request.getParameterValues(name); 
                } 
                 
            4、如果参数名去掉了前后缀后不是一org.Apache.struts开头则把参数存到定义好的HashMap里 
                // Populate parameters, except "standard" struts attributes 
                // such as 'org.apache.struts.action.CANCEL' 
                if (!(stripped.startsWith("org.apache.struts."))) { 
                    properties.put(stripped, parameterValue); 
                } 
                 
        8、调用BeanUtils的方法把formBean的属性填充进去(异常处理那块不是很明白) 
            // Set the corresponding properties of our bean 
            try { 
                BeanUtils.populate(bean, properties); 
            } catch (Exception e) { 
                throw new ServletException("BeanUtils.populate", e); 
            } finally { 
                if (multipartHandler != null) { 
                    // Set the multipart request handler for our ActionForm. 
                    // If the bean isn't an ActionForm, an exception would have been 
                    // thrown earlier, so it's safe to assume that our bean is 
                    // in fact an ActionForm. 
                    ((ActionForm) bean).setMultipartRequestHandler(multipartHandler); 
                } 
            } 
             
    7、加入合适的话就把退出属性设置到request里;(还是不了解) 
        // Set the cancellation request attribute if appropriate 
        if ((request.getParameter(Globals.CANCEL_PROPERTY) != null) 
            || (request.getParameter(Globals.CANCEL_PROPERTY_X) != null)) { 
            request.setAttribute(Globals.CANCEL_KEY, Boolean.TRUE); 
        } 
         
18、验证表单输入的合法性。如果有不合法的则return。(一般不用struts的表单级验证) 
    // Validate any fields of the ActionForm bean, if applicable 
    try { 
        if (!processValidate(request, response, form, mapping)) { 
            return; 
        } 
    } catch (InvalidCancelException e) { 
        ActionForward forward = processException(request, response, e, form, mapping); 
        processForwardConfig(request, response, forward); 
        return; 
    } catch (IOException e) { 
        throw e; 
    } catch (ServletException e) { 
        throw e; 
    } 
     
19、处理mapping指定的forward 和 include 
    // Process a forward or include specified by this mapping 
    if (!processForward(request, response, mapping)) { 
        return; 
    } 
    if (!processInclude(request, response, mapping)) { 
        return; 
    } 
     
20、创建或者获取一个Action的实例来处理请求。 
    // Create or acquire the Action instance to process this request 
    Action action = processActionCreate(request, response, mapping); 
     
    1、从mapping里取出配置的Action类名 
        // Acquire the Action instance we will be using (if there is one) 
        String className = mapping.getType(); 
     
    2、把查找Action实例的动作记入到日志里 
        if (log.isDebugEnabled()) { 
            log.debug(" Looking for Action instance for class " + className); 
        } 

    3、在拿到Action实例之前先线程同步synchronized (actions) ,保证只有一个Action实例 
     
    4、从map里取出Action返回,(如果有的话),并把结果写入日志 
        nstance = (Action) actions.get(className); 

        if (instance != null) { 
            if (log.isTraceEnabled()) { 
                log.trace("  Returning existing Action instance"); 
            } 

            return (instance); 
        } 
     
    5、如果上面的操作没进行,那说明要新建一个Action实例,把新建实例的动作记录到日志里 
        if (log.isTraceEnabled()) { 
            log.trace("  Creating new Action instance"); 
        } 
         
    6、创建出Action实例,吧实例放到map里并返回实例 
        try { 
                instance = (Action) RequestUtils.applicationInstance(className); 

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

            actions.put(className, instance); 

            if (instance.getServlet() == null) { 
                instance.setServlet(this.servlet); 
            } 
        } 

        return (instance); 
         
21、再次判断Action是否创建成功,如果没有则方法直接return 
        if (action == null) { 
            return; 
        } 
         
22、执行Action的excute方法,获得ActionForward 
        // Call the Action instance itself 
        ActionForward forward = processActionPerform(request, response, action, form, mapping); 
        其中processActionPerform方法调用了action的excute方法: 
        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)); 
            } 
        } 
    这里也做了一个处理,如果要在执行excute方法之前做一些操作,就可以覆盖processActionPerform方法。 
     
23、更具Actionforward进行转发 
        // Process the returned ActionForward instance 
        processForwardConfig(request, response, forward); 
     
        1、如果ActionForward为空,则方法直接返回 
            if (forward == null) { 
                return; 
            } 
             
        2、把接下来处理forward的操作记录到日志里 
            if (log.isDebugEnabled()) { 
                log.debug("processForwardConfig(" + forward + ")"); 
            } 
         
        3、从mapping里获取forward对应的url,默认用forward的方式转发,如果配了redirect,则用redirect重定向 
            String forwardPath = forward.getPath(); 
            String uri; 

            // If the forward can be unaliased into an action, then use the path of the action 
            String actionIdPath = RequestUtils.actionIdURL(forward, request, servlet); 
            if (actionIdPath != null) { 
                forwardPath = actionIdPath; 
                ForwardConfig actionIdForward = new ForwardConfig(forward); 
                actionIdForward.setPath(actionIdPath); 
                forward = actionIdForward; 
            } 

            // paths not starting with / should be passed through without any 
            // processing (ie. they're absolute) 
            if (forwardPath.startsWith("/")) { 
                // get module relative uri 
                uri = RequestUtils.forwardURL(request, forward, null); 
            } else { 
                uri = forwardPath; 
            } 

            if (forward.getRedirect()) { 
                // 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