• struts请求源码的跟踪


      最近工作任务不是很紧,时间也不能白白浪费,以前常用的struts2框架源码没去了解过,所以就跟踪一下struts2的整个执行过程.由于本人也是抱着学习的态度来阅读掩码,若文章在表述和代码方面如有不妥之处,欢迎批评指正。留下你的脚印,欢迎评论!希望能互相学习。

     我这里的struts2源码是maven导jar包来查看源码的,这样方便多了,可以在IDE下查看源码。pom.xml文件如下

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
      <modelVersion>4.0.0</modelVersion>
      <groupId>Struts_test_lishun</groupId>
      <artifactId>Struts_test_lishun</artifactId>
      <version>0.0.1-SNAPSHOT</version>
      <packaging>war</packaging>
       <dependencies>
        <dependency>
            <groupId>commons-logging</groupId>
            <artifactId>commons-logging</artifactId>
            <version>1.2</version>
        </dependency>
        <dependency>
            <groupId>org.apache.struts</groupId>
            <artifactId>struts2-core</artifactId>
            <version>2.3.24</version>
        </dependency>
      </dependencies>
    </project>

    1.实现从web.xml说起:

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
         <!-- 添加一个过滤器-->
        <filter>
            <filter-name>action2</filter-name>
            <!-- struts2处理过程的入口 -->
            <filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
            <init-param>
                <param-name >struts.serve.static.browserCache</param-name>
                <param-value>true</param-value>
            </init-param>
            <!-- 初始化参数(访问后缀为.do),这里的param-name都可以从后面提到的源码中查看到,具体的其他功能本人也不是全部了解,
                                有兴趣的可以自己再去深入,-->
             <init-param>
                <param-name>struts.action.extension</param-name>
                <param-value>do</param-value>
            </init-param>
        </filter>
    
        <filter-mapping>
            <filter-name>action2</filter-name>
            <!-- 过滤所有的请求 -->
            <url-pattern>/*</url-pattern>
        </filter-mapping>
    </web-app>

    2:开始查看StrutsPrepareAndExecuteFilter 过滤器

    2-1:初始化:(tomcat一启动就会执行到这里)

    public void init(FilterConfig filterConfig) throws ServletException {
            InitOperations init = new InitOperations();
            Dispatcher dispatcher = null;
            try {
           //封装filterConfig,便于对xml文件的操作
                FilterHostConfig config = new FilterHostConfig(filterConfig);
           //初始化日志
                init.initLogging(config);
           //初始化dispatcher,struts2的核心类,大部分的操作都是在这里完成的
                dispatcher = init.initDispatcher(config);
                init.initStaticContentLoader(config, dispatcher);
              //初始化当前类属性:prepare 、execute(这两个属性会在doFilter里设置ActionContext和encoding的值)
                prepare = new PrepareOperations(dispatcher);
                execute = new ExecuteOperations(dispatcher);
                this.excludedPatterns = init.buildExcludedPatternsList(dispatcher);
           //这是是空函数。说是回调方法,这里不太懂,有了解可以给小弟指引下
                postInit(dispatcher, filterConfig);
            } finally {
                if (dispatcher != null) {
                    dispatcher.cleanUpAfterInit();
                }
                init.cleanup();
            }
        }

    2-2:对上面init.initDispatcher(config)的跟踪:

    public Dispatcher initDispatcher( HostConfig filterConfig ) {
            //这里主要是对新建一个dispatcher,并加载配置文件中的初始化参数
            Dispatcher dispatcher = createDispatcher(filterConfig);
            //初始化dispatcher
            dispatcher.init();
            return dispatcher;
    }

    2-2-1:dispatcher.init()跟踪:

    public void init() {
    
            if (configurationManager == null) {
                //把struts.xml文件进行封装(默认是名字是struts)
                configurationManager = createConfigurationManager(DefaultBeanSelectionProvider.DEFAULT_BEAN_NAME);
            }
    
            try {            init_FileManager();
                //加载org/apache/struts2/default.properties
                init_DefaultProperties(); // [1]
                //加载struts-default.xml,struts-plugin.xml,struts.xml
                init_TraditionalXmlConfigurations(); // [2]
                //下面的初始化代码。没去研究,到此已经把web.xml,和struts.xml文件给加载进来了
                init_LegacyStrutsProperties(); // [3]
                init_CustomConfigurationProviders(); // [5]
                init_FilterInitParameters() ; // [6]
                init_AliasStandardObjects() ; // [7]
    
                Container container = init_PreloadConfiguration();
                //注入dispatcher
                container.inject(this);
                init_CheckWebLogicWorkaround(container);
    
                if (!dispatcherListeners.isEmpty()) {
                    for (DispatcherListener l : dispatcherListeners) {
                        l.dispatcherInitialized(this);
                    }
                }
                errorHandler.init(servletContext);
    
            } catch (Exception ex) {
                if (LOG.isErrorEnabled())
                    LOG.error("Dispatcher initialization failed", ex);
                throw new StrutsException(ex);
            }
        }

    初始化就在此告一段落了。

    ----------------------华丽的分割线-------------------------

    3-1:访问某个action,StrutsPrepareAndExecuteFilter对所有的的请求都会过滤,此事就跟踪到;

    public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
    
            HttpServletRequest request = (HttpServletRequest) req;
            HttpServletResponse response = (HttpServletResponse) res;
    
            try {
                if (excludedPatterns != null && prepare.isUrlExcluded(request, excludedPatterns)) {
                    chain.doFilter(request, response);
                } else {
                    //设置类属性编码和国际化
                    prepare.setEncodingAndLocale(request, response);
                    //设置类属性action的上下文
                    prepare.createActionContext(request, response);
                    prepare.assignDispatcherToThread();
                    //对request进行封装,继续跟踪下去代码会发现这里的作用就是根据不同的请求返回不同request的封装类(这里就是用到装饰者模式)
                    request = prepare.wrapRequest(request);
             //返回ActionMapping:里面有通过struts.xml文件通过反射获取到action对应的类和方法
                    ActionMapping mapping = prepare.findActionMapping(request, response, true);
             //若返回的ActionMapping为null,则表示不是调用action
                    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);
            }
        }

    3-2:追踪代码-生成映射action的映射:

    ActionMapping mapping = prepare.findActionMapping(request, response, true);
    在PrepareOperations中,主要执行ActionMapper的getMapping(..)方法
    public ActionMapping findActionMapping(HttpServletRequest request, HttpServletResponse response, boolean forceLookup) {
            ActionMapping mapping = (ActionMapping) request.getAttribute(STRUTS_ACTION_MAPPING_KEY);
            if (mapping == null || forceLookup) {
                try {
             //new一个ActionMapper实例并执行getMapping(..)方法
                    mapping = dispatcher.getContainer().getInstance(ActionMapper.class).getMapping(request, dispatcher.getConfigurationManager());
                    if (mapping != null) {
                        request.setAttribute(STRUTS_ACTION_MAPPING_KEY, mapping);
                    }
                } catch (Exception ex) {
                    dispatcher.sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, ex);
                }
            }
    
            return mapping;
        }

    3-2-1:追踪到ActionMapper的getMapping(..)方法;而ActionMapper是一个接口,方法由DefaultActionMapper类实现;

    定位到DefaultActionMapper类的getMapping(..);

    public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) {
            ActionMapping mapping = new ActionMapping();
            //获取到请求的url
            String uri = RequestUtils.getUri(request);
            //截取url,把含有";"的后面的字符截取掉
            int indexOfSemicolon = uri.indexOf(";");
            uri = (indexOfSemicolon > -1) ? uri.substring(0, indexOfSemicolon) : uri;
              
            //继续截取url,url的后缀截取掉(如.action和.do等),在这里就可以看见extensions变量,struts默认的后缀名就是.action
            uri = dropExtension(uri, mapping);
            if (uri == null) {
                return null;
            }
            //从url获取namespace和name并和mapping匹配
            parseNameAndNamespace(uri, mapping, configManager);
            handleSpecialParameters(request, mapping);
            return parseActionName(mapping);
        }

    3-3:现在返回到doFilter方法,追踪到

    execute.executeAction(request, response, mapping);方法;
     public void executeAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping) throws ServletException {
            dispatcher.serviceAction(request, response, mapping);
        }

    最终调用的是Dispatcher的serviceAction(...)方法,

     

    public void serviceAction(HttpServletRequest request, HttpServletResponse response, ActionMapping mapping)
                throws ServletException {
            //封转上下文环境,主要将request、params、session等Map封装成为一个上下文Map、
            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 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);
                //从mapping(这里是已经封装好的mapping,struts.xml与请求url共同映射出来的数据)中获取命名空间
                String namespace = mapping.getNamespace();
            //从mapping中获取action的name
                String name = mapping.getName();
           //从mapping中获取请求方法(是获取动态的请求方法:在url后面加上 ‘!+方法名’)
                String method = mapping.getMethod();
           //生成action的代理类,执行页面参数值和根据反射执行请求方法
                ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(
                        namespace, name, method, extraContext, true, false);
    
                request.setAttribute(ServletActionContext.STRUTS_VALUESTACK_KEY, proxy.getInvocation().getStack());
    
                //如果配置文件中执行的这个action配置了result,就直接转到result
                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) {
                logConfigurationException(request, e);
                sendError(request, response, HttpServletResponse.SC_NOT_FOUND, e);
            } catch (Exception e) {
                if (handleException || devMode) {
                    sendError(request, response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e);
                } else {
                    throw new ServletException(e);
                }
            } finally {
                UtilTimerStack.pop(timerKey);
            }
        }

     

    3-3-1:追踪  ActionProxy proxy = getContainer().getInstance(ActionProxyFactory.class).createActionProxy(

                        namespace, name, method, extraContext, true, false);

    创建ActionProxy是由ActionProxyFactory实现类完成

    DefaultActionProxyFactory createActionProxy方法

    public ActionProxy createActionProxy(String namespace, String actionName, String methodName, Map<String, Object> extraContext, boolean executeResult, boolean cleanupContext) {
            
            ActionInvocation inv = new DefaultActionInvocation(extraContext, true);
            container.inject(inv);
            //StrutsActionProxyFactory 的createActionProxy方法,   StrutsActionProxyFactory是DefaultActionProxyFactory的子类,调用的是StrutsActionProxyFactory的方法
            return createActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
        }

    --------- StrutsActionProxyFactory-----------------

    public class StrutsActionProxyFactory extends DefaultActionProxyFactory {
    
        @Override
        public ActionProxy createActionProxy(ActionInvocation inv, String namespace, String actionName, String methodName, boolean executeResult, boolean cleanupContext) {
            
            StrutsActionProxy proxy = new StrutsActionProxy(inv, namespace, actionName, methodName, executeResult, cleanupContext);
            container.inject(proxy);
    //继续跟踪代码: prepare()调用的是父类DefaultActionProxy的prepare()方法
            proxy.prepare();
            return proxy;
        }
    }

    --------------------DefaultActionProxy-------------------

    protected void prepare() {
            String profileKey = "create DefaultActionProxy: ";
            try {
                UtilTimerStack.push(profileKey);
                config = configuration.getRuntimeConfiguration().getActionConfig(namespace, actionName);
    
                if (config == null && unknownHandlerManager.hasUnknownHandlers()) {
                    config = unknownHandlerManager.handleUnknownAction(namespace, actionName);
                }
                if (config == null) {
                    throw new ConfigurationException(getErrorMessage());
                }
                  //获取执行method为空的方法名, 若为空则默认设置为"execute" 
                resolveMethod();
    
                if (!config.isAllowedMethod(method)) {
                    throw new ConfigurationException("Invalid method: " + method + " for action " + actionName);
                }
                  //创建action
                invocation.init(this);
    
            } finally {
                UtilTimerStack.pop(profileKey);
            }
        }

    ----------------- invocation.init(this)--------------

    public void init(ActionProxy proxy) {
            this.proxy = proxy;
            Map<String, Object> contextMap = createContextMap();
    
            // Setting this so that other classes, like object factories, can use the ActionProxy and other
            // contextual information to operate
            ActionContext actionContext = ActionContext.getContext();
    
            if (actionContext != null) {
                actionContext.setActionInvocation(this);
            }
             //这里开始创建action
            createAction(contextMap);
    
            if (pushAction) {
                stack.push(action);
                contextMap.put("action", action);
            }
    
            invocationContext = new ActionContext(contextMap);
            invocationContext.setName(proxy.getActionName());
    
            // get a new List so we don't get problems with the iterator if someone changes the list
            List<InterceptorMapping> interceptorList = new ArrayList<InterceptorMapping>(proxy.getConfig().getInterceptors());
            interceptors = interceptorList.iterator();
        }

    至此action就创建成功了,然后就是给action的属性赋值和执行action里的方法

    3-4:重新回到Dispatcher的serviceAction(...)方法,当mapping.getResult() != null此时就跟踪到

    proxy.execute();方法

    ActionProxy是接口,execute()由StrutsActionProxy实现;

    public String execute() throws Exception {
            ActionContext previous = ActionContext.getContext();
            ActionContext.setContext(invocation.getInvocationContext());
            try {
                //开始执行action和注入属性值,由DefaultActionInvocation实现这个方法
                return invocation.invoke();
            } finally {
                if (cleanupContext)
                    ActionContext.setContext(previous);
            }
        }

    ---------------DefaultActionInvocation--------------------------------

    .....其他代码省略
    try {
        //最主要执行了intercept方法,这里就是执行设置action的属性值和执行方法,由接口Interceptor的抽象类AbstractInterceptor的子类MethodFilterInterceptor执行intercept方法,
        //由于AbstractInterceptor的实现类很多,所以这段代码会执行很多次,至于为什么会执行多次,本人也还在研究,若有仁兄了解可以给小弟一点指引
    resultCode = interceptor.getInterceptor().intercept(DefaultActionInvocation.this); } finally {   UtilTimerStack.pop(interceptorMsg); } .....其他代码省略

    ---------------MethodFilterInterceptor---------------------------------

    @Override
        public String intercept(ActionInvocation invocation) throws Exception {
            if (applyInterceptor(invocation)) {
              //执行了ParametersInterceptor的doIntercept方法,
     return doIntercept(invocation);
            } 
            return invocation.invoke();
        }

    ---------------ParametersInterceptor---------------------

     

    @Override
        public String doIntercept(ActionInvocation invocation) throws Exception {
            Object action = invocation.getAction();
            if (!(action instanceof NoParameters)) {
                ActionContext ac = invocation.getInvocationContext();
                final Map<String, Object> parameters = retrieveParameters(ac);
    
                if (LOG.isDebugEnabled()) {
                    LOG.debug("Setting params " + getParameterLogMap(parameters));
                }
    
                if (parameters != null) {
                    Map<String, Object> contextMap = ac.getContextMap();
                    try {
                        ReflectionContextState.setCreatingNullObjects(contextMap, true);
                        ReflectionContextState.setDenyMethodExecution(contextMap, true);
                        ReflectionContextState.setReportingConversionErrors(contextMap, true);
    
                        ValueStack stack = ac.getValueStack();
    //给action的参数赋值
                        setParameters(action, stack, parameters);
                    } finally {
                        ReflectionContextState.setCreatingNullObjects(contextMap, false);
                        ReflectionContextState.setDenyMethodExecution(contextMap, false);
                        ReflectionContextState.setReportingConversionErrors(contextMap, false);
                    }
                }
            }
    //最后执行action
            return invocation.invoke();
        }

    至此,整个action的请求就结束了;

     

  • 相关阅读:
    [翻译] GoogleMaterialDesignIcons
    [翻译] InstagramPhotoPicker
    UIButton的resizableImageWithCapInsets使用解析
    [翻译] RAReorderableLayout
    [翻译] ZLSwipeableView
    【转】php利用mkdir创建多级目录
    【转】用 PHP 内置函数 file_put_contents 写入文件
    【转】PHP 之 CURL 模拟登陆并获取数据
    【转】php curl 伪造IP来源的实例代码
    【转】POP3、SMTP和IMAP之间的区别和联系
  • 原文地址:https://www.cnblogs.com/lishun1005/p/4748006.html
Copyright © 2020-2023  润新知