1)Struts2的Action,配置在struts.xml文件中,web容器(这里我使用的是Tomcat)启动时,是不会初始化的,只有有web请求时,才会初始化相应的action,每一次请求都会有一个新的action被实例化,所以不用担心线程安全的问题。

2)要使用Struts2必须要配置过滤器,过滤器在web.xml文件中配置。

       <!--
  1、使用struts2必须有这个filter,而且必须拦截所有的web请求[/*],而且必须出现在所有的filter的最后一位
  2、使用了struts2之后,就不需要自己设置请求编码了
  3、现在用的StrutsPrepareAndExecuteFilter,是struts2.1以后的叫法
   而在最开始,FilterDispatcher
  -->
    <filter>
        <filter-name>struts2</filter-name>
        <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>struts2</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

       配置<url-pattern>/*</url-pattern>是拦截所有的web请求。

  最近要面试web开发的岗位,所以抽空看了一看Struts2.3.1.2的源码,顺便写了一个demo

  其他知识点

  1)在window下,可以在/bin/catalina.bate配置JVM参数,在linux下,可以在/bin/catalina.bsh配置JVM参数。

  2)Tomcat启动的时候,会加载自身的web.xml和web应用的web.xml这两个配置文件的并集,包括里面配置的一些servlet。

  3)在web应用程序中配置好StrutsPrepareAndExecuteFilter以及映射关系(拦截所有的web请求),自己实际debug调试源码,启动Tomcat,会执行StrutsPrepareAndExecuteFilter的init(FilterConfig filterConfig)方法(包括初始化disPatcher),Tomcat启动的过程中,会加载jsp文件,同时也会为每个jsp文件执行一次doFilter(ServletRequest req,ServletResponse res,FilterChain chain),包括上下文环境的准备,以及Action的映射关系(调用DefaultActionMapper中的getMapping方法获取ActionMapping)。Action的映射关系是读取Jsp页面配置的以.action结尾的action属性的配置。

 4)此时Tomcat已经启动,View层,HTTP请求,触发一个action,就会执行一次doFilter方法,初始化上下文环境,以及寻找action对应的映射关系,最后再通过ObjectFactory的buildAction方法,实例化actionbean,最后压入到OGNL栈中,如果是同一个用户,点击同一个链接,会再一次的实例化一次actionbean,而不会用以前的那个,这样就不会担心线程的安全性。

5)Struts2 Action实例化的源码分析

   1.StrutsPrepareAndExecuteFilter的doFilter方法 

public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest request = (HttpServletRequest) req;
        HttpServletResponse response = (HttpServletResponse) res;

        try {

            //上下文环境的准备
            prepare.setEncodingAndLocale(request, response);
            prepare.createActionContext(request, response);
            prepare.assignDispatcherToThread();
   if ( excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
    chain.doFilter(request, response);
   } else {
    request = prepare.wrapRequest(request);
    ActionMapping mapping = prepare.findActionMapping(request, response, true);
    if (mapping == null) {
     boolean handled = execute.executeStaticResourceRequest(request, response);
     if (!handled) {
      chain.doFilter(request, response);
     }
    } else {

     //实例化action的入口
     execute.executeAction(request, response, mapping);
    }
   }
        } finally {
            prepare.cleanupRequest(request);
        }
    }

2. ExecuteOperations的executeAction方法,直接调用Dispatcher的serviceAction方法,响应action服务。

    /**
     * Executes an action
     * @throws ServletException
     */
    public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {

        //实例化action的入口
        dispatcher.serviceAction(request, response, servletContext, mapping);
    }

3. Dispatcher中serviceAction

   /**
     * Load Action class for mapping and invoke the appropriate Action method, or go directly to the Result.
     * <p/>
     * This method first creates the action context from the given parameters,
     * and then loads an <tt>ActionProxy</tt> from the given action name and namespace.
     * After that, the Action method is executed and output channels through the response object.
     * Actions not found are sent back to the user via the {@link Dispatcher#sendError} method,
     * using the 404 return code.
     * All other errors are reported by throwing a ServletException.
     *
     * @param request  the HttpServletRequest object
     * @param response the HttpServletResponse object
     * @param mapping  the action mapping object
     * @throws ServletException when an unknown error occurs (not a 404, but typically something that
     *                          would end up as a 5xx by the servlet container)
     * @param context Our ServletContext object
     */
    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ServletContext context,
                              ActionMapping mapping) throws ServletException {

        Map<String, Object> extraContext = createContextMap(request, response, mapping, context);

        // If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
        ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
        boolean nullStack = stack == null;
        if (nullStack) {
            ActionContext ctx = ActionContext.getContext();
            if (ctx != null) {
                stack = ctx.getValueStack();
            }
        }
        if (stack != null) {
            extraContext.put(ActionContext.VALUE_STACK, valueStackFactory.createValueStack(stack));
        }

        String timerKey = "Handling request from Dispatcher";
        try {
            UtilTimerStack.push(timerKey);
            String namespace = mapping.getNamespace();
            String name = mapping.getName();
            String method = mapping.getMethod();

            Configuration config = configurationManager.getConfiguration();
            ActionProxy proxy = config.getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                    namespace, name, method, extraContext, true, false);

            request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());

            // if the ActionMapping says to go straight to a result, do it!
            if (mapping.getResult() != null) {
                Result result = mapping.getResult();
                result.execute(proxy.getInvocation());
            } else {
                proxy.execute();
            }

            // If there was a previous value stack then set it back onto the request
            if (!nullStack) {
                request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
            }
        } catch (ConfigurationException e) {
            // WW-2874 Only log error if in devMode
            if(devMode) {
                String reqStr = request.getRequestURI();
                if (request.getQueryString() != null) {
                    reqStr = reqStr + "?" + request.getQueryString();
                }
                LOG.error("Could not find action or result\n" + reqStr, e);
            }
            else {
                    if (LOG.isWarnEnabled()) {
                LOG.warn("Could not find action or result", e);
                    }
            }
            sendError(request, response, context, HttpServletResponse.SC_NOT_FOUND, e);
        } catch (Exception e) {
            sendError(request, response, context, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
        } finally {
            UtilTimerStack.pop(timerKey);
        }
    }



Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐