struts2 运行过程及集成原理

这个过程是从客户端发起访问,到服务端响应,并返回结果的整个过程。
以struts-2.5.5作为研究对象

第一部分 从StrutsPrepareAndExecuteFilter开始

 StrutsPrepareAndExecuteFilter作为struts2的一个核心过滤器(Filter),接受所有客户端的请求,经过一系列的处理才到我们的Action。对于Filter,他首先执行init方法初始化,因此我们先看看StrutsPrepareAndExecuteFilter的初始化做了什么。

InitOperations
Dispatcher
Container
ActionMapping
ActionProxy
Interceptor

StrutsPrepareAndExecuteFilter.init 

public void init(FilterConfig filterConfig) throws ServletException {
        //初始化InitOperations 
        InitOperations  init = new InitOperations();
        Dispatcher dispatcher = null;
        try {
             //获取web.xml的Filter内的init-param参数配置
            FilterHostConfig config = new FilterHostConfig(filterConfig);
            init.initLogging(config);
            dispatcher = init.initDispatcher(config);
            init.initStaticContentLoader(config, dispatcher);
            //当前请求运行之前的包含初始化的操作--仅产生PrepareOperations对象
            prepare = new PrepareOperations(dispatcher);
            //所有过滤器所包含运行操作--仅产生ExecuteOperations对象
            execute = new ExecuteOperations(dispatcher);
            this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);

            postInit(dispatcher, filterConfig);
        } finally {
            if (dispatcher != null) {
                dispatcher.cleanUpAfterInit();
            }
            init.cleanup();
        }
    }
 接着我们看具体dispatcher 的创建过程,createDispatcher方法。 
 InitOperations.initDispatcher 
  public Dispatcher initDispatcher( HostConfig filterConfig ) {
    **Dispatcher** dispatcher = createDispatcher(filterConfig);
    dispatcher.init();
    return dispatcher;
  }
  InitOperations.createDispatcher
  private Dispatcher createDispatcher( HostConfig filterConfig ) {
    Map<String, String> params = new HashMap<>();
     //遍历filterConfig把init-param全部拿出来,就是那些在web.xml配置在Filter标签里面的初始化参数
    for ( Iterator e = filterConfig.getInitParameterNames(); e.hasNext(); ) {
        String name = (String) e.next();
        String value = filterConfig.getInitParameter(name);
        params.put(name, value);
    }
     //构造dispatcher
    return new Dispatcher(filterConfig.getServletContext(), params);
}
Dispatcher.init

  //Load configurations, including both XML and zero-configuration strategies,
// and update optional settings, including whether to reload configurations and resource files.

public void init() {

    if (configurationManager == null) {
        configurationManager =      createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
    }

    try {
        init_FileManager();
        init_DefaultProperties(); //加入默认配置供给器,解析org/apache/struts2/default.properties
        init_TraditionalXmlConfigurations(); // [2] struts-default.xml 
        init_LegacyStrutsProperties(); // [3] struts-plugin.xml
        init_CustomConfigurationProviders(); // [5] struts.properties
        init_FilterInitParameters() ; // [6] default.properties
        init_AliasStandardObjects() ; // [7] struts.custom.properties
           //  预加载配置,构建容器
           //  构造出容器,因此容器是拥有这些常量和配置里面指定的bean的实例化的能力。说到容器的实例化的能力,其实实例化的过程有一部分是依赖于容器的依赖注入功能的,容器实例化类的时候是通过构造函数注入(ConstructorInjector)方式,通过反射获取指定类的构造函数,如果有无参数的构造函数就直接使用无参数构造函数完成初始化,如果只有有参数的构造函数,就分析参数上面有没有注入的标志,如果有就通过容器的参数注入(ParameterInjector)方式注入依赖值给这个参数,然后通过这些有值的参数调用这个构造函数,达到实例化的目地,实例化之后还需要对其成员进行注入依赖,容器对成员注入是使用成员注入(MemberInjector,分别有MethodInjector和FieldInjector,针对方法还是属性判断使用哪一种方式)的方式,对静态成员还有静态注入(StaticInjector,其实质是通过MemberInjector实现的,只不过不需要穿入实例对象)方式;对于容器提供的依赖注入能力,其前提条件就是使用注入的类要配置在bean里面,也就是说类要交给容器管理,并且在使用注入依赖的属性、参数、方法要使用@Inject注解去指明,否则没有办法注入,或者注入了null,或者出现异常。
         然后通过container.getInstance(ObjectFactory.class) , 创建出我们的ObjectFactory,使用默认工厂对象创建这个ObjectFactory的实例,根据之前的介绍,可以看到struts2的struts.objectFactory被注释了,并没有指定,意味着是使用struts作为name的ObjectFactory的bean,就是使用org.apache.struts2.impl.StrutsObjectFactory作为真正的ObjectFactory实现。 
        我们之后的Action、Result、Interceptor等等一系列与我们密切相关的对象都是通过ObjectFactory创建的,它可以说是struts的心脏。**ObjectFactory实例化对象的默认方式不是使用容器的,而是通过一般的Class.newIntance()方法**,而注入才是借助容器的inject(Container.inject)方法给实例化后的对象注入依赖值,对于这样的操作,一个解析就是避免影响核心类的性能,如果把所有的Action都交给容器来管理,会造成容器过于庞大,因为有时候Action可能会是成千上万的,这样会拖慢容器的执行;另外一个原因,因为Action、Result、Interceptor等都是需要多例的,而非单例,方便起见就直接用ObjectFactory,通过Class.newIntance()创建一个实例好了;估计还有一个方面想就是为了扩展,因为如果都交给容器,其他集成框架无法获取到struts2的容器的,无法操作他,所以为了扩展就使用了Class.newIntance()方法实例化。 

  **Container** container = init_PreloadConfiguration();
        container.inject(this);
        init_CheckWebLogicWorkaround(container);
          //触发监听器初始化事件
        if (!dispatcherListeners.isEmpty()) {
            for (DispatcherListener l : dispatcherListeners) {
                l.dispatcherInitialized(this);
            }
        }
        errorHandler.init(servletContext);

    } catch (Exception ex) {
        LOG.error("Dispatcher initialization failed", ex);
        throw new StrutsException(ex);
    }
}

Dispatcher.init_CustomConfigurationProviders
//加入自定义的供给器
private void init_CustomConfigurationProviders() {
    String configProvs = initParams.get("configProviders");
    if (configProvs != null) {
        String[] classes = configProvs.split("\\s*[,]\\s*");
        for (String cname : classes) {
            try {
                Class cls = ClassLoaderUtil.loadClass(cname, this.getClass());
                ConfigurationProvider prov = (ConfigurationProvider)cls.newInstance();
                if (prov instanceof ServletContextAwareConfigurationProvider) {
                      ((ServletContextAwareConfigurationProvider)prov).initWithContext(servletContext);
                }
                configurationManager.addContainerProvider(prov);
            } catch (InstantiationException e) {
                throw new ConfigurationException("Unable to instantiate provider: "+cname, e);
            } catch (IllegalAccessException e) {
                throw new ConfigurationException("Unable to access provider: "+cname, e);
            } catch (ClassNotFoundException e) {
                throw new ConfigurationException("Unable to locate provider class: "+cname, e);
            }
        }
    }
}
    从上面代码可以看出struts2对配置文件的加载顺序: 
           1、org/apache/struts2/default.properties 
           2、struts-default.xml 
           3、struts-plugin.xml(如果有) 
           4、struts.xml 
           5、struts.properties(如果有) 
           6、default.properties(如果有) 
           7、struts.custom.properties(如果有) 
        org/apache/struts2/default.properties是配置了struts2的常量的默认值,比如struts.i18n.encoding 、struts.action.extension、struts.i18n.reload等常量。 
        struts-default.xml里面配置了struts2的核心bean,以及用constant配置的常量,比如ObjectFactory、ActionProxyFactory、ActionMapper等bean。 
        struts-plugin.xml是struts2的插件的配置文件,比如struts2-spring-plugin。 
        struts.xml是使用struts2时候基本配置,比如action、result、interceptor、constant等。 
        struts.properties、default.properties、struts.custom.properties是原来旧版本的一些配置。 
        注意:后面加载的配置文件可以覆盖前面的配置文件的配置 
         从上面代码可以知道,init_XXXX的方法是为配置管理器收集配置供给器的信息,而最后调用init_PreloadConfiguration方法,构建出配置对象和容器,他们是struts2中非常重要的两个对象,配置对象里面包含了struts2的所有的配置信息,容器里面包含了struts2中所有的对象的工厂对象。以后使用到的Action、拦截器(Interceptor)、Result等对象都是通过他们两个协同创建出来的。 

         全部的配置都在这里解析完成,并能够组织起来,构成运行时的结构了
#第二部分:doFilter的工作过程           


  *StrutsPrepareAndExecuteFilter.doFilter*

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

    HttpServletRequest request = (HttpServletRequest) req;
    HttpServletResponse response = (HttpServletResponse) res;
try {
        String uri = RequestUtils.getUri(request);
        if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
            LOG.trace("Request {} is excluded from handling by Struts, passing request to other filters", uri);
            chain.doFilter(request, response);
        } else {
            LOG.trace("Checking if {} is a static resource", uri);
               *protected ExecuteOperations execute;*
              boolean handled = execute.executeStaticResourceRequest(request, response);
            if (!handled) {
                LOG.trace("Assuming uri {} as a normal action", uri);
                prepare.setEncodingAndLocale(request, response);
                prepare.createActionContext(request, response);
                prepare.assignDispatcherToThread();
                request = prepare.wrapRequest(request);
                `**ActionMapping**` mapping = prepare.findActionMapping(request, response, true);
                if (mapping == null) {
                    LOG.trace("Cannot find mapping for {}, passing to other filters", uri);
                    chain.doFilter(request, response);
                } else {
                    LOG.trace("Found mapping {} for {}", mapping, uri);
                    execute.executeAction(request, response, mapping);
                }
            }
        }
    } finally {
        prepare.cleanupRequest(request);
    }
}
*ActionMapping*
public ActionMapping(String name, String namespace, String method, Map<String, Object> params) {
    this.name = name;
    this.namespace = namespace;
    this.method = method;
    this.params = params;
}


   *// Tries to execute a request for a static resource*
     public boolean executeStaticResourceRequest(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
    // there is no action in this request, should we look for a static resource?
    String resourcePath = RequestUtils.getServletPath(request);

    if ("".equals(resourcePath) && null != request.getPathInfo()) {
        resourcePath = request.getPathInfo();
    }

    StaticContentLoader staticResourceLoader = dispatcher.getContainer().getInstance(StaticContentLoader.class);
    if (staticResourceLoader.canHandle(resourcePath)) {
        staticResourceLoader.findStaticResource(resourcePath, request, response);
        // The framework did its job here
        return true;

    } else {
        // this is a normal request, let it pass through
        return false;
    }
}
 *ExecuteOperations类*
public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
    dispatcher.serviceAction(request, response, mapping);
}
        *Dispatcher类*
        //Dispatcher.serviceAction是我们所有action的统一处理入口,进入这个方法,就是开始了action的处理了。Dispatcher.serviceAction主要是完成以下几个工作, 
           1、创建Action对象,以代理对象的形式 
           2、执行mapping.execute或者开始调用拦截器(就是proxy.execute);如果执行了mapping.execute(执行Result返回客户端),就停止下面操作了 
           3、通过反射调用Action的指定方法,并返回 
           4、执行Result返回客户端,可以是页面(视图)跳转或者其他操作 
        这些都是重点啊!我们先总体看看Dispatcher.serviceAction方法 

 public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
        throws ServletException {

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

    *// If there was a previous value stack, then create a new copy and pass it in to be used by the new Action
     //重新创建ValueStack,这里的ValueStack是我们后面的action用到的那个ValueStack*
    ValueStack stack = (ValueStack) request.getAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY);
    boolean nullStack = stack == null;
    if (nullStack) {
          //用指定的ValueStack来创建一个新的ActionContext,并设置为本线程的ActionContext,
  //注意这里已经不是之前的引导容器创建的ActionContext了,而是我们action使用的那个ActionContext,
  //引导容器的ActionContext已经在DefaultConfiguration.reloadContainer方法退出时候被清空了,
  //现在的ValueStack和ActionContext都是全新的。
        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();
        //创建ActionProxy动态代理
        **ActionProxy** proxy = 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) {
           //匹配到的action方法比如 login 
             如果是result.execute直接执行的情况,到这里doFilter对一个请求的处理过程就执行完了
            Result result = mapping.getResult();
             result.execute(proxy.getInvocation());
        } else {
            //action中默认执行的方法
            //proxy.execute,我们action执行的过程,当然在action真正执行之前,当然还有拦截器的执行,然后action执行完后返回一个resultCode,再根据resultCode找到对应的Result,执行页面(视图)跳转,返回客户端之前当然还会生成视图
            proxy.execute();
        }**

        // If there was a previous value stack then set it back onto the request
        if (!nullStack) {
             //设置值栈
             //再把修改过的ValueStack设置到request中,在Result之后返回视图时候可以用得着这些参数,
             //因为Action执行方法时候可能会改变了ValueStack的里面的值
            request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, stack);
        }
    } catch (ConfigurationException e) {
        logConfigurationException(request, e);
        sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
    } catch (Exception e) {
        e.printStackTrace();
        if (handleException || devMode) {
            sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
        } else {
            throw new ServletException(e);
        }
    } finally {
        UtilTimerStack.pop(timerKey);
    }
}


       *DefaultActionInvocatio默认action代理类*
        public String invoke() throws Exception {
       String profileKey = "invoke: ";
       try {
          UtilTimerStack.push(profileKey);
          //防止多次执行action,保证action只执行一次
          if (executed) {
             throw new IllegalStateException("Action has already executed");
          }
          //通过之前的拦截器映射迭代器迭代取出拦截器映射对象
          if (interceptors.hasNext()) {
             //获取拦截器映射对象
             final InterceptorMapping interceptor = (InterceptorMapping) interceptors.next();
             String interceptorMsg = "interceptor: " + interceptor.getName();
             UtilTimerStack.push(interceptorMsg);
             try {
                //通过映射对象获取拦截器,执行**Interceptor拦截器**,注意,intercept参数是当前的action调用对象,
                //其实这里面也调用DefaultActionInvocation.invoke,他们这样是递归调用,通过这样的方法遍历完        interceptors
                resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
             } finally {
                UtilTimerStack.pop(interceptorMsg);
             }
          } else {
             //当遍历完interceptors时候,执行正真的action操作,这里是一个重点
             //返回的resultCode会用来匹配对应的Result
            resultCode = invokeActionOnly();
          }

          //action是否已经被调用,用来判断是否第一次执行Result操作,这样写是因为action执行完后,
          //拦截器会一个一个执行下面的操作,然后返回上一个调用的拦截器,就是按调用拦截器时候反过来的顺序执行,
          //这是**递归的原理**,所以如果每个拦截器都执行Result那么程序就会出错,
          //所以当action执行完Result之后,就通过这种方式不允许拦截器执行Result
          if (!executed) { //第一次执行Result有效
             //执行相关的监听器,Result执行前的监听器
             if (preResultListeners != null) {
                for (Object preResultListener : preResultListeners) {
                   PreResultListener listener = (PreResultListener) preResultListener;
                   String _profileKey = "preResultListener: ";
                   try {
                      UtilTimerStack.push(_profileKey);
                      listener.beforeResult(this, resultCode);
                   } finally {
                      UtilTimerStack.pop(_profileKey);
                   }
                }
             }

             //如果支持执行Result,就执行,这个是构建action代理对象时候传入去的参数,是true
             if (proxy.getExecuteResult()) {
                //执行我们的Result,这里是另一个重点
                executeResult();
             }
             //标识已经被执行过
             executed = true;
          }
          //返回执行代码
          return resultCode;
           } finally {
              UtilTimerStack.pop(profileKey);
           }
    }

  当拦截器执行完时候,执行action的地方,在上面我们看到了 resultCode = invokeActionOnly()

    *DefaultActionInvocation默认action代理类*
   public String invokeActionOnly() throws Exception {
    return invokeAction(getAction(), proxy.getConfig());
   }
    *DefaultActionInvocation默认action代理类*
    protected String invokeAction(Object action, ActionConfig actionConfig) throws Exception {
    String methodName = proxy.getMethod();

    LOG.debug("Executing action method = {}", methodName);

    String timerKey = "invokeAction: " + proxy.getActionName();
    try {
        UtilTimerStack.push(timerKey);

        Object methodResult;
        try {
            //获取action,然后根据methodName的名称,通过反射获取这个方法对象,这个是没有参数的方法
            methodResult = ognlUtil.callMethod(methodName + "()", getStack().getContext(), action);
        } catch (MethodFailedException e) {
            // if reason is missing method,  try checking UnknownHandlers
            if (e.getReason() instanceof NoSuchMethodException) {
                if (unknownHandlerManager.hasUnknownHandlers()) {
                    try {
                      //执行这个方法,设置action为执行实例,大多数情况都是执行这个方法,返回值大多数情况也是String类型
                        methodResult = unknownHandlerManager.handleUnknownMethod(action, methodName);
                    } catch (NoSuchMethodException ignore) {
                        // throw the original one
                        throw e;
                    }
                } else {
                    // throw the original one
                    throw e;
                }
                // throw the original exception as UnknownHandlers weren't able to handle invocation as well
                if (methodResult == null) {
                    throw e;
                }
            } else {
                // exception isn't related to missing action method, throw it
                throw e;
            }
        }
        return saveResult(actionConfig, methodResult);
    } catch (NoSuchPropertyException e) {
        throw new IllegalArgumentException("The " + methodName + "() is not defined in action " + getAction().getClass() + "");
    } catch (MethodFailedException e) {
        // We try to return the source exception.
        Throwable t = e.getCause();

        if (actionEventListener != null) {
            String result = actionEventListener.handleException(t, getStack());
            if (result != null) {
                return result;
            }
        }
        if (t instanceof Exception) {
            throw (Exception) t;
        } else {
            throw e;
        }
    } finally {
        UtilTimerStack.pop(timerKey);
    }
}
    从上面可以看出,这个就是我们的Action的方法执行的地方,然后根据返回值,如果这个值是Result类型,就直接用这个Result来执行会面的操作;如果是String类型,就是resultCode,就用这个resultCode来获取Result,就是匹配result标签里面的name,这些配置早就储存在result配置对象了,然后用获取到的Result执行下面的操作,我们回到DefaultActionInvocation.invoke,看executeResult里面的处理过程
       *DefaultActionInvocation默认action代理类*
    public String invoke() throws Exception {
    String profileKey = "invoke: ";
    try {
        UtilTimerStack.push(profileKey);

        if (executed) {
            throw new IllegalStateException("Action has already executed");
        }

        if (interceptors.hasNext()) {
            final InterceptorMapping interceptor = interceptors.next();
            String interceptorMsg = "interceptor: " + interceptor.getName();
            UtilTimerStack.push(interceptorMsg);
            try {
                resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this);
            } finally {
                UtilTimerStack.pop(interceptorMsg);
            }
        } else {
            resultCode = **invokeActionOnly**();
        }

        // this is needed because the result will be executed, then control will return to the Interceptor, which will
        // return above and flow through again
        if (!executed) {
            if (preResultListeners != null) {
                LOG.trace("Executing PreResultListeners for result [{}]", result);

                for (Object preResultListener : preResultListeners) {
                    PreResultListener listener = (PreResultListener) preResultListener;

                    String _profileKey = "preResultListener: ";
                    try {
                        UtilTimerStack.push(_profileKey);
                        listener.beforeResult(this, resultCode);
                    }
                    finally {
                        UtilTimerStack.pop(_profileKey);
                    }
                }
            }

            // now execute the result, if we're supposed to
            if (proxy.getExecuteResult()) {
                **executeResult();**
            }

            executed = true;
        }

        return resultCode;
    }
    finally {
        UtilTimerStack.pop(profileKey);
    }
}
    *DefaultActionInvocation默认action代理类*
   private void executeResult() throws Exception {
   //先创建一个Result
   result = createResult();

    String timerKey = "executeResult: " + getResultCode();
    try {
        UtilTimerStack.push(timerKey);
        if (result != null) {
            //如果创建成功
            result.execute(this);
        } else if (resultCode != null && !Action.NONE.equals(resultCode)) { //如果有返回值,并且Action不是没有视图的就抛出异常
            throw new ConfigurationException("No result defined for action " + getAction().getClass().getName() + " and result " + getResultCode(), proxy.getConfig());
        } else {  //没有视图,就是通过response直接写回响应给客户端哪些情况,比如json
            if (LOG.isDebugEnabled()) {
                LOG.debug("No result returned for action {} at {}", getAction().getClass().getName(), proxy.getConfig().getLocation());
            }
        }
    } finally {
        UtilTimerStack.pop(timerKey);
    }
}

“`

上面情况我们先看看createResult方法,我们假设返回的Result是一个org.apache.struts2.dispatcher.ServletDispatcherResult类型,就是默认类型,Action的默认页面(视图)跳转方式,需要返回值,有视图

当创建Result成功就可以执行了,前面说过假设是返回默认的Result ———— org.apache.struts2.dispatcher.ServletDispatcherResult,我们针对ServletDispatcherResult.execute来展开

result执行完了,跳转到指定的页面(视图),然后正真生成好页面,再返回给客户端

好累呀

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值