zoukankan      html  css  js  c++  java
  • 层层递进Struts1(五)之处理流程

        这篇博客我们深入Struts框架执行部分源码,从ActionServlet的process函数开始,看一下其内在的执行过程。

        流程图

        以下流程图展示的是ActionServlet和RequestProcessor两个类用到的函数,如RequestProcessor调用的其它类的函数不再说明。

        

        

    函数说明

        我们选择几个重要的函数说明,其它函数则简单说明一下即可。

        ActionServlet

        process

        /**
         * <p>Perform the standard request processing for this request, and create
         * the corresponding response.</p>
         *
         * @param request The servlet request we are processing
         * @param response The servlet response we are creating
         *
         * @exception IOException if an input/output error occurs
         * @exception ServletException if a servlet exception is thrown
         */
        protected void process(HttpServletRequest request, HttpServletResponse response)
            throws IOException, ServletException {
    
            ModuleUtils.getInstance().selectModule(request, getServletContext());
            ModuleConfig config = getModuleConfig(request);
    
            RequestProcessor processor = getProcessorForModule(config);
            if (processor == null) {
               processor = getRequestProcessor(config);
            }
            processor.process(request, response);
    
        }
    
    }

        在调试时首先进入这个函数(Tomcat启动完毕,产生请求后),这个函数的作用是获取加载阶段产生的Module对象,产生struts逻辑处理的主要对象RequestProcessor。

        RequestProcessor

        process

       /**
         * <p>Process an <code>HttpServletRequest</code> and create the
         * corresponding <code>HttpServletResponse</code> or dispatch
         * to another resource.</p>
         *
         * @param request The servlet request we are processing
         * @param response The servlet response we are creating
         *
         * @exception IOException if an input/output error occurs
         * @exception ServletException if a processing exception occurs
         */
        public void process(HttpServletRequest request,
                            HttpServletResponse response)
            throws IOException, ServletException {
    
            // Wrap multipart requests with a special wrapper
            request = processMultipart(request);
    
            // Identify the path component we will use to select a mapping
            String path = processPath(request, response);
            if (path == null) {
                return;
            }
            
            if (log.isDebugEnabled()) {
                log.debug("Processing a '" + request.getMethod() +
                          "' for path '" + path + "'");
            }
    
            // Select a Locale for the current user if requested
            processLocale(request, response);
    
            // Set the content type and no-caching headers if requested
            processContent(request, response);
            processNoCache(request, response);
    
            // General purpose preprocessing hook
            if (!processPreprocess(request, response)) {
                return;
            }
            
            this.processCachedMessages(request, response);
    
            // Identify the mapping for this request
            ActionMapping mapping = processMapping(request, response, path);
            if (mapping == null) {
                return;
            }
    
            // Check for any role required to perform this action
            if (!processRoles(request, response, mapping)) {
                return;
            }
    
            // Process any ActionForm bean related to this request
            ActionForm form = processActionForm(request, response, mapping);
            processPopulate(request, response, form, mapping);
            
            // 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;
            }
                
            // Process a forward or include specified by this mapping
            if (!processForward(request, response, mapping)) {
                return;
            }
            
            if (!processInclude(request, response, mapping)) {
                return;
            }
    
            // Create or acquire the Action instance to process this request
            Action action = processActionCreate(request, response, mapping);
            if (action == null) {
                return;
            }
    
            // Call the Action instance itself
            ActionForward forward =
                processActionPerform(request, response,
                                     action, form, mapping);
    
            // Process the returned ActionForward instance
            processForwardConfig(request, response, forward);
    
        }
    

        process是RequestProcessor对象主要的逻辑处理函数,根据上面的流程图可以看到,整个逻辑处理都是在这个函数中完成,它所调用的函数实现的功能如下:

        processMultipart

        这个函数的作用是判断是否是文件上传请求,如果是则特殊处理。

        /**
         * <p>If this is a multipart request, wrap it with a special wrapper.
         * Otherwise, return the request unchanged.</p>
         *
         * @param request The HttpServletRequest we are processing
         */
        protected HttpServletRequest processMultipart(HttpServletRequest request) {
    
            if (!"POST".equalsIgnoreCase(request.getMethod())) {
                return (request);
            }
            
            String contentType = request.getContentType();
            if ((contentType != null) &&
                contentType.startsWith("multipart/form-data")) {
                return (new MultipartRequestWrapper(request));
            } else {
                return (request);
            }
    
        }

        processPath

        获取并截取请求,处理后变为需要的字符串,例如请求:http://localhost:8080/struts_login/login.do,处理后的字符串为/login.do。

       /**
         * <p>Identify and return the path component (from the request URI) that
         * we will use to select an <code>ActionMapping</code> with which to dispatch.
         * If no such path can be identified, create an error response and return
         * <code>null</code>.</p>
         *
         * @param request The servlet request we are processing
         * @param response The servlet response we are creating
         *
         * @exception IOException if an input/output error occurs
         */
        protected String processPath(HttpServletRequest request,
                                     HttpServletResponse response)
            throws IOException {
    
            String path = null;
    
            // For prefix matching, match on the path info (if any)
            path = (String) request.getAttribute(INCLUDE_PATH_INFO);
            if (path == null) {
                path = request.getPathInfo();
            }
            if ((path != null) && (path.length() > 0)) {
                return (path);
            }
    
            // For extension matching, strip the module prefix and extension
            path = (String) request.getAttribute(INCLUDE_SERVLET_PATH);
            if (path == null) {
                path = request.getServletPath();
            }
            String prefix = moduleConfig.getPrefix();
            if (!path.startsWith(prefix)) {
                String msg = getInternal().getMessage("processPath");
                
                log.error(msg + " " + request.getRequestURI());
                response.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
    
                return null;
            }
            
            path = path.substring(prefix.length());
            int slash = path.lastIndexOf("/");
            int period = path.lastIndexOf(".");
            if ((period >= 0) && (period > slash)) {
                path = path.substring(0, period);
            }
            return (path);
    
        }

        设置相关函数

        processLocale、processContent、processNoCache、processCachedMessages,这四个函数的作用分别是:设置国际化文件、设置内容类型、设置取消缓存、设置清楚session中struts的错误信息。

        processMapping

        根据上面生成的path,实例化对应的ActionMapping对象,如果此对象不为空则加载到request中,对应名称为Globals.MAPPING_KEY,如果为空,发送异常并添加到response中。

            // If a mapping is found, put it in the request and return it
            if (mapping != null) {
                request.setAttribute(Globals.MAPPING_KEY, mapping);
                return (mapping);
            }
    
            // 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);
                }
            }

        processRoles

        Action执行是否需要特定的角色权限,如果不需要,则继续执行。

            String roles[] = mapping.getRoleNames();
            if ((roles == null) || (roles.length < 1)) {
                return (true);
            }

        processActionForm

        创建ActionForm并检测此Action的作用域,如果是Request则添加到request中,如果是Session则添加到session中。

           // 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相关

        processPopulate这个函数的作用是:调用processPopulate()方法,如果存在为ActionMapping配置的ActionForm,则封装请求对象中的数据到ActionForm 中,在进行封装之前,先调用ActionForm 的reset()方法进行属性值的默认化

        processValidate:如果action元素的属性validate被设置为true ,则进一步调用validate()方法进行规则校验。如果validate()方法校验失败,就会保存一个ActionErrors 对象到请求区域中,请求将会自动重定向到action映射的input属性所指定的页面中;如果校验通过或在action 映射中没有配置ActionForm,则继续处理请求。

        异常处理

        process这个函数中,包含一个try...catch块,如果出现InvalidCancelException则执行两个函数。

        processException,将异常写入日志警告文件,并跑出异常;processForwardConfig,与下面最后执行的一个函数相同,捕获结束

        跳转路径

        processForward、processInclude:这个两个函数的作用是,检测struts-config下<action>元素的forward和include属性的值,如有配置,则把forward和include 请求放在配置的页面内;processForward()调用 RequestDispatcher.forward(),而processInclude()调用RequestDispatcher.include()。

        如果同时配置了forward 和include 属性,Struts会优先选择forward。

        processActionCreate

        这个函数的作用是从struts-config下<action>的type属性得到Action类名,创建并返回它的实例。

            Action instance = null;
            synchronized (actions) {
    
                // Return any existing Action instance of this class
                instance = (Action) actions.get(className);
                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);

        processActionPerform

        执行自己写的Action中execute函数,当然包含跳转逻辑:

            try {
                return (action.execute(mapping, form, request, response));
            } catch (Exception e) {
                return (processException(request, response,
                                         e, form, mapping));
            }

        processForwardConfig

        获取即将跳转的路径:
            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;
            }

        doForward

        获取RequestDispatcher,并执行跳转。
            if (request instanceof MultipartRequestWrapper) {
                request = ((MultipartRequestWrapper) request).getRequest();
            }
    
            RequestDispatcher rd = getServletContext().getRequestDispatcher(uri);
            if (rd == null) {
                response.sendError(
                    HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
                    getInternal().getMessage("requestDispatcher", uri));
                return;
            }
            rd.forward(request, response);

    总结

        如果我们忽略函数,只从宏观上看,可以总结为:

    • 读取配置文件
    • 获取访问地址
    • 设置Struts
    • 创建、赋值、验证ActionForm
    • 异常处理
    • 创建Action
    • 执行Action中的逻辑
    • 页面跳转

        至此,如果不追究到最底层的函数,struts的执行流程已经讲解完毕,如果有问题,欢迎大家指出。

               更多相关博客,请至《层层递进Struts1(八)之总结》


  • 相关阅读:
    查看端口有没有被占用
    微信公众号2()
    How to insert a segment of noise to music file
    puppet practice
    Docker Commands
    LempelZiv algorithm realization
    The algorithm of entropy realization
    Java network programmingguessing game
    Deploy Openstack with RDO and Change VNC console to Spice
    puppet overview
  • 原文地址:https://www.cnblogs.com/riskyer/p/3221605.html
Copyright © 2011-2022 走看看