struts1.3源碼學習

  最近在看struts1.3源碼,做個記錄

執行流程:

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章