博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
struts请求源码的跟踪
阅读量:6253 次
发布时间:2019-06-22

本文共 16705 字,大约阅读时间需要 55 分钟。

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

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

4.0.0
Struts_test_lishun
Struts_test_lishun
0.0.1-SNAPSHOT
war
commons-logging
commons-logging
1.2
org.apache.struts
struts2-core
2.3.24

1.实现从web.xml说起:

action2
org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter
struts.serve.static.browserCache
true
struts.action.extension
do
action2
/*

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
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
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
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
interceptorList = new ArrayList
(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
parameters = retrieveParameters(ac); if (LOG.isDebugEnabled()) { LOG.debug("Setting params " + getParameterLogMap(parameters)); } if (parameters != null) { Map
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的请求就结束了;

 

 

转载于:https://www.cnblogs.com/lishun1005/p/4748006.html

你可能感兴趣的文章
两张图片合成一张_ps技巧:大光比照片后期曝光合成技法
查看>>
码条形码属性_条码生成器如何批量生成code 11码
查看>>
和lua的效率对比测试_不同编程语言能耗不同?看这27种语言对比!
查看>>
让某控件失去焦点_常用基本控件测试用例(一)
查看>>
天气模式_今年台风活跃期即将结束!下周天气将开启“大变脸”模式
查看>>
扫码枪关闭常亮模式_生意好时最怕收银出故障,这几个扫码枪的常见问题你一定要知道...
查看>>
如何双击打开vivado工程_利用TCL重建vivado工程
查看>>
mysql的引双向链表_Mysql高手系列 - 第22篇:mysql索引原理详解(高手必备技能)
查看>>
mysql in 查询改成_MySQL not in嵌套查询改写成外连接方式
查看>>
mysql community安装_MySQL Community Server 5.7安装详细步骤
查看>>
python处理多行字符串_python多行字符串
查看>>
java冒泡排序_用java写个冒泡排序?
查看>>
linux 开发java_做开发环境的操作系统是 Linux 好还是 Windows 好?
查看>>
java正文提取_Java网页正文提取工具
查看>>
blocked java_使用jstack排查多线程死锁、阻塞
查看>>
centos7 nohup无法保持_CentOS 不间断会话(ssh关闭后如何保证程序继续运行)(nohup和screen)...
查看>>
显示图片java代码_用于显示下载或是显示图片的一段代码
查看>>
java scrip_js java scrip基本语法
查看>>
java 加上天数_java日期时间加上天数 | 学步园
查看>>
java 爬楼梯算法_9.1 爬楼梯
查看>>