zoukankan      html  css  js  c++  java
  • struts1.3执行流程分析

      这是在去年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);
                }


    在读源码的时候难免会出现一些理解上的错误,如果哪位发现了上面写的不对,麻烦告诉我一下。

  • 相关阅读:
    证券市场主体
    证券投资基金
    1.监控系统的重要性
    1.五种世界顶级思维-20190303
    【四校联考】【比赛题解】FJ NOIP 四校联考 2017 Round 7
    【学长出题】【比赛题解】17-09-29
    【codeforces】【比赛题解】#854 CF Round #433 (Div.2)
    【codeforces】【比赛题解】#851 CF Round #432 (Div.2)
    【算法学习】三分法
    【codeforces】【比赛题解】#849 CF Round #431 (Div.2)
  • 原文地址:https://www.cnblogs.com/coffee/p/1675726.html
Copyright © 2011-2022 走看看