(四)Strut1.x原理剖析

本文详细解析了Struts1.x的工作原理及处理流程,包括配置文件的加载过程及用户请求的处理流程。

一、基础回顾

这里先回顾一下Struts1.x的基础,到底一个最简单的Struts程序包含哪些部分?

  • ActionServlet:它是整个Struts框架的中央控制器,包括请求的接收、分发、处理和页面跳转。它是Struts自带的Servlet类,需要在web.xml中配置,并初始化config参数,这个参数指定struts配置文件的位置。
  • struts配置文件:是一个xml格式的文件,不配置的话默认是/WEB-INF/struts-config.xml。
  • ActionForm:封装、验证客户提交过来的表单数据,并传入Action中。
  • Action:用户自定义的Control类,需要继承Action类并重写execute方法,在execute方法里面进行逻辑代码的调用和处理,并返回ActionForward控制页面跳转。
  • ActionForward:根据配置文件的标签配置的数据,控制页面跳转。

好了,有了以上部分,我们来猜测一下它的处理流程。众所周知,Tomcat启动时就会加载web.xml文件,根据Servlet的生命周期,Tomcat启动时就会创建Servlet的实例,并调用它的init()方法。因为整个Web项目是从服务器启动开始的,所以整个Struts程序就从ActionServlet开始。因为struts配置文件的路径会作为ActionServlet的初始化参数配进去,所以struts配置文件也是在服务器启动时解析并加载的。

初始化完成后,接下来,就轮到接受用户的请求了。很明显,ActionForm就是用来封装用户请求数据的,所以用户请求后第一个站出来操作的就是ActionForm。

ActionForm接收了数据之后对整个流程有什么推进作用呢?当然是将用户数据交给Action进行业务处理啦。为了让用户看到结果,Action处理完成之后肯定要跳转并渲染前端页面,然后将前端页面返回给用户的。

说了一大堆废话,好像看得有点懵了,接下来我们用一张图梳理一下上面这些话:
这里写图片描述

以上只是我们对业务流程的猜想,从原理上讲已经八九不离了,因为每个环节都环环相扣,接下来我们从源码验证。

二、Struts源码解析

首先,Struts的所有配置都是在Tomcat启动时加载进来的,因为web.xml中配置了ActionServlet的config初始化参数,而它的值正是struts配置文件的路径。所以不难看出ActionServlet是Struts的中心控制器,原因有两点:

  1. 它在服务器启动时便创建了实例并运行init()方法,相当于整个程序的始祖;
  2. 在Tomcat服务器中,每个项目最先请求的只有Servlet(jsp本质上也是Servlet),所以每个用户请求实际上都是交给Servlet操作的。

好吧,上面说了那么多都只是为了说明一点:ActionServlet是Struts源码分析的切入点。

1、从配置加载开始

以下是配置解析加载的核心代码:

protected ModuleConfig initModuleConfig(String prefix, String paths) throws ServletException {
        if(log.isDebugEnabled()) {
            log.debug("Initializing module path \'" + prefix + "\' configuration from \'" + paths + "\'");
        }

        //创建工厂类
        ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();
        //创建配置组件
        ModuleConfig config = factoryObject.createModuleConfig(prefix);
        //创建并初始化文件解析器
        Digester digester = this.initConfigDigester();
        //解析多个配置文件路径,存到列表中
        List urls = this.splitAndResolvePaths(paths);
        Iterator i = urls.iterator();

        while(i.hasNext()) {
            URL url = (URL)i.next();
            digester.push(config);
            //从列表取出配置文件路径,读取并解析文件,保存在config模块中
            this.parseModuleConfigFile(digester, url);
        }

        //将ModuleConfig存到ServletContext的一个属性中
        this.getServletContext().setAttribute("org.apache.struts.action.MODULE" + config.getPrefix(), config);
        return config;
    }

以上的注释已经把配置文件的解析说得很清楚了,这里我们用一张图捋一下处理流程。
这里写图片描述

到这里,各个类的工作基本上就明晰了,Digester负责解析,不过重中之重就是:ModuleConfig存储了所有Struts配置

2、处理用户请求

接下来就到了最重要的一部分,Struts是怎样处理用户请求的?我们都知道,Servlet的处理方法主要是service(),或者doGet()和doPost(),所以我们就从这几个方法入手。而doGet()和doPost()都调用了process(request, response)方法,所以先来看一下ActionServlet的process方法。

protected void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        //根据prefix选取ModuleConfig,将MessageResources和ModuleConfig一并存在request中
ModuleUtils.getInstance().selectModule(request, this.getServletContext());
//在request或context中获取config
ModuleConfig config = this.getModuleConfig(request);
//获取模块的Processor(处理器),保存在request中
RequestProcessor processor = this.getProcessorForModule(config);
if(processor == null) {
    processor = this.getRequestProcessor(config);
}
processor.process(request, response);

    }

由上述代码看出,这个方法主要做了两个操作:

  • 根据请求获取config
  • 根据config获取processor
    接下来,就执行processor的process方法,这是这个process方法的重头戏,代码如下:
    public void process(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
        //判断是否为Multipart请求,是则将request指定为MultipartRequestWrapper,否则还是HttpServletRequest
        request = this.processMultipart(request);
        //解析出请求路径
        String path = this.processPath(request, response);
        if(path != null) {
            if(log.isDebugEnabled()) {
                log.debug("Processing a \'" + request.getMethod() + "\' for path \'" + path + "\'");
            }
            //将客户端语言环境保存到session中
            this.processLocale(request, response);
            // response.setContentType(contentType);
            this.processContent(request, response);
            //设置响应头无缓存
            this.processNoCache(request, response);
            if(this.processPreprocess(request, response)) {
                //把之前的ActionMessage移除
                this.processCachedMessages(request, response);
                //根据请求path在ModuleConfig中获取ActionConfig,存在request中
                ActionMapping mapping = this.processMapping(request, response, path);
                if(mapping != null) {
                    if(this.processRoles(request, response, mapping)) {
                        //创建ActionForm并存在request或session中
                        ActionForm form = this.processActionForm(request, response, mapping);
                        //填充ActionForm的参数
                        this.processPopulate(request, response, form, mapping);

                        ActionForward forward;
                        try {
                            //执行校验,若执行完,则return;否则,继续执行
                            if(!this.processValidate(request, response, form, mapping)) {
                                return;
                            }
                        } catch (InvalidCancelException var8) {
                            //当校验时抛出异常时,进行异常处理
                            forward = this.processException(request, response, var8, form, mapping);
                            this.processForwardConfig(request, response, forward);
                            return;
                        } catch (IOException var9) {
                            throw var9;
                        } catch (ServletException var10) {
                            throw var10;
                        }
                        //如果配置的action标签中配置了forward属性或include属性,则直接跳转
                        if(this.processForward(request, response, mapping)) {
                            if(this.processInclude(request, response, mapping)) {
                                //创建action,加了锁,线程安全,每个action只有一个实例,用HashMap存储
                                Action action = this.processActionCreate(request, response, mapping);
                                if(action != null) {
                                    //执行Action的execute方法。ActionMessages保存在request域中
                                    forward = this.processActionPerform(request, response, action, form, mapping);
                                    //控制页面跳转
                                    this.processForwardConfig(request, response, forward);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

接下来用一幅图来阐述整个流程:

这里写图片描述

分析了源代码之后,发现我们的猜想跟它的处理流程已经八九不离十了,接下来我们再从源码来阐述一遍它的处理流程:

  1. 解析请求,给response和session做一些默认设置,并根据请求路径获取mapping,即映射。
  2. 创建ActionForm并填充数据
  3. 如果设置指明不执行校验或者无校验方法,则继续下一步;否则,执行校验,并跳转返回。如果校验期间出现异常,则执行异常处理方法后返回。
  4. 如果配置的action标签中配置了forward属性或include属性,则直接跳转;否则,执行创建Action并执行它的execute方法,期间将可以ActionMessages保存在request域,然后返回forward后进行页面跳转。

以上是我对Struts1.x核心源码的解析,如有疏漏之处还请指正。

分析以下html 使用这个js 实现markdown转思维导图生成html: <!doctype html> <html> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Markmap</title> <style> * { margin: 0; padding: 0; } html { font-family: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; } #mindmap { display: block; width: 100vw; height: 100vh; } .markmap-dark { background: #27272a; color: white; } </style> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.12/dist/style.css"> <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.18/dist/katex.min.css"> </head> <body> <svg id="mindmap"></svg> <script src="https://cdn.jsdelivr.net/npm/d3@7.9.0/dist/d3.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/markmap-view@0.18.12/dist/browser/index.js"></script> <script src="https://cdn.jsdelivr.net/npm/markmap-toolbar@0.18.12/dist/index.js"></script> <script>(e=>{window.WebFontConfig={custom:{families:["KaTeX_AMS","KaTeX_Caligraphic:n4,n7","KaTeX_Fraktur:n4,n7","KaTeX_Main:n4,n7,i4,i7","KaTeX_Math:i4,i7","KaTeX_Script","KaTeX_SansSerif:n4,n7,i4","KaTeX_Size1","KaTeX_Size2","KaTeX_Size3","KaTeX_Size4","KaTeX_Typewriter"]},active:()=>{e().refreshHook.call()}}})(()=>window.markmap)</script><script src="https://cdn.jsdelivr.net/npm/webfontloader@1.6.28/webfontloader.js" defer></script><script>(()=>{setTimeout(()=>{const{markmap:S,mm:Q}=window,$=new S.Toolbar;$.attach(Q);const I=$.render();I.setAttribute("style","position:absolute;bottom:20px;right:20px"),document.body.append(I)})})()</script> <script>((l,U,M,R)=>{const N=l();window.mm=N.Markmap.create("svg#mindmap",(U||N.deriveOptions)(R),M),window.matchMedia("(prefers-color-scheme: dark)").matches&&document.documentElement.classList.add("markmap-dark")})(()=>window.markmap,null,{"content":"markmap","children":[{"content":"Links","children":[{"content":"<a href=\"https://markmap.js.org/\">Website</a>","children":[],"payload":{"tag":"li","lines":"8,9"}},{"content":"<a href=\"https://github.com/gera2ld/markmap\">GitHub</a>","children":[],"payload":{"tag":"li","lines":"9,11"}}],"payload":{"tag":"h2","lines":"6,7"}},{"content":"Related Projects","children":[{"content":"<a href=\"https://github.com/gera2ld/coc-markmap\">coc-markmap</a> for Neovim","children":[],"payload":{"tag":"li","lines":"13,14"}},{"content":"<a href=\"https://marketplace.visualstudio.com/items?itemName=gera2ld.markmap-vscode\">markmap-vscode</a> for VSCode","children":[],"payload":{"tag":"li","lines":"14,15"}},{"content":"<a href=\"https://github.com/emacs-eaf/eaf-markmap\">eaf-markmap</a> for Emacs","children":[],"payload":{"tag":"li","lines":"15,17"}}],"payload":{"tag":"h2","lines":"11,12"}},{"content":"Features","children":[{"content":"Lists","children":[{"content":"<strong>strong</strong> <s>del</s> italic <mark>highlight</mark>","children":[],"payload":{"tag":"li","lines":"23,24"}},{"content":"<code>inline code</code>","children":[],"payload":{"tag":"li","lines":"24,25"}},{"content":"<svg width=\"16\" height=\"16\" viewBox=\"0 -3 24 24\"><path d=\"M19 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2V5a2 2 0 0 0-2-2m-9 14-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8z\"/></svg> checkbox","children":[],"payload":{"tag":"li","lines":"25,26"}},{"content":"Katex: <span class=\"katex\"><span class=\"katex-mathml\"><math xmlns=\"http://www.w3.org/1998/Math/MathML\"><semantics><mrow><mi>x</mi><mo>=</mo><mfrac><mrow><mo>−</mo><mi>b</mi><mo>±</mo><msqrt><mrow><msup><mi>b</mi><mn>2</mn></msup><mo>−</mo><mn>4</mn><mi>a</mi><mi>c</mi></mrow></msqrt></mrow><mrow><mn>2</mn><mi>a</mi></mrow></mfrac></mrow><annotation encoding=\"application/x-tex\">x = {-b \\pm \\sqrt{b^2-4ac} \\over 2a}</annotation></semantics></math></span><span class=\"katex-html\" aria-hidden=\"true\"><span class=\"base\"><span class=\"strut\" style=\"height:0.4306em;\"></span><span class=\"mord mathnormal\">x</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span><span class=\"mrel\">=</span><span class=\"mspace\" style=\"margin-right:0.2778em;\"></span></span><span class=\"base\"><span class=\"strut\" style=\"height:1.3845em;vertical-align:-0.345em;\"></span><span class=\"mord\"><span class=\"mord\"><span class=\"mopen nulldelimiter\"></span><span class=\"mfrac\"><span class=\"vlist-t vlist-t2\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:1.0395em;\"><span style=\"top:-2.655em;\"><span class=\"pstrut\" style=\"height:3em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mord mtight\">2</span><span class=\"mord mathnormal mtight\">a</span></span></span></span><span style=\"top:-3.23em;\"><span class=\"pstrut\" style=\"height:3em;\"></span><span class=\"frac-line\" style=\"border-bottom-width:0.04em;\"></span></span><span style=\"top:-3.394em;\"><span class=\"pstrut\" style=\"height:3em;\"></span><span class=\"sizing reset-size6 size3 mtight\"><span class=\"mord mtight\"><span class=\"mord mtight\">−</span><span class=\"mord mathnormal mtight\">b</span><span class=\"mbin mtight\">±</span><span class=\"mord sqrt mtight\"><span class=\"vlist-t vlist-t2\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.9221em;\"><span class=\"svg-align\" style=\"top:-3em;\"><span class=\"pstrut\" style=\"height:3em;\"></span><span class=\"mord mtight\" style=\"padding-left:0.833em;\"><span class=\"mord mtight\"><span class=\"mord mathnormal mtight\">b</span><span class=\"msupsub\"><span class=\"vlist-t\"><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.7463em;\"><span style=\"top:-2.786em;margin-right:0.0714em;\"><span class=\"pstrut\" style=\"height:2.5em;\"></span><span class=\"sizing reset-size3 size1 mtight\"><span class=\"mord mtight\">2</span></span></span></span></span></span></span></span><span class=\"mbin mtight\">−</span><span class=\"mord mtight\">4</span><span class=\"mord mathnormal mtight\">a</span><span class=\"mord mathnormal mtight\">c</span></span></span><span style=\"top:-2.8821em;\"><span class=\"pstrut\" style=\"height:3em;\"></span><span class=\"hide-tail mtight\" style=\"min-width:0.853em;height:1.08em;\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"400em\" height=\"1.08em\" viewBox=\"0 0 400000 1080\" preserveAspectRatio=\"xMinYMin slice\"><path d=\"M95,702\nc-2.7,0,-7.17,-2.7,-13.5,-8c-5.8,-5.3,-9.5,-10,-9.5,-14\nc0,-2,0.3,-3.3,1,-4c1.3,-2.7,23.83,-20.7,67.5,-54\nc44.2,-33.3,65.8,-50.3,66.5,-51c1.3,-1.3,3,-2,5,-2c4.7,0,8.7,3.3,12,10\ns173,378,173,378c0.7,0,35.3,-71,104,-213c68.7,-142,137.5,-285,206.5,-429\nc69,-144,104.5,-217.7,106.5,-221\nl0 -0\nc5.3,-9.3,12,-14,20,-14\nH400000v40H845.2724\ns-225.272,467,-225.272,467s-235,486,-235,486c-2.7,4.7,-9,7,-19,7\nc-6,0,-10,-1,-12,-3s-194,-422,-194,-422s-65,47,-65,47z\nM834 80h400000v40h-400000z\"/></svg></span></span></span><span class=\"vlist-s\">​</span></span><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.1179em;\"><span></span></span></span></span></span></span></span></span></span><span class=\"vlist-s\">​</span></span><span class=\"vlist-r\"><span class=\"vlist\" style=\"height:0.345em;\"><span></span></span></span></span></span><span class=\"mclose nulldelimiter\"></span></span></span></span></span></span>","children":[{"content":"<a href=\"#?d=gist:af76a4c245b302206b16aec503dbe07b:katex.md\">More Katex Examples</a>","children":[],"payload":{"tag":"li","lines":"27,28"}}],"payload":{"tag":"li","lines":"26,28","fold":1}},{"content":"Now we can wrap very very very very long text with the <code>maxWidth</code> option","children":[],"payload":{"tag":"li","lines":"28,29"}},{"content":"Ordered list","children":[{"content":"1. item 1","children":[],"payload":{"tag":"li","lines":"30,31","listIndex":1}},{"content":"2. item 2","children":[],"payload":{"tag":"li","lines":"31,33","listIndex":2}}],"payload":{"tag":"li","lines":"29,33"}}],"payload":{"tag":"h3","lines":"21,22"}},{"content":"Blocks","children":[{"content":"<pre data-lines=\"35,38\"><code class=\"language-js\"><span class=\"hljs-variable language_\">console</span>.<span class=\"hljs-title function_\">log</span>(<span class=\"hljs-string\">&apos;hello, JavaScript&apos;</span>)\n</code></pre>","children":[],"payload":{"tag":"pre","lines":"35,38"}},{"content":"<table data-lines=\"39,43\">\n<thead data-lines=\"39,40\">\n<tr data-lines=\"39,40\">\n<th>Products</th>\n<th>Price</th>\n</tr>\n</thead>\n<tbody data-lines=\"41,43\">\n<tr data-lines=\"41,42\">\n<td>Apple</td>\n<td>4</td>\n</tr>\n<tr data-lines=\"42,43\">\n<td>Banana</td>\n<td>2</td>\n</tr>\n</tbody>\n</table>","children":[],"payload":{"tag":"table","lines":"39,43"}},{"content":"<img src=\"https://markmap.js.org/favicon.png\" alt>","children":[],"payload":{"tag":"img","lines":"44,45"}}],"payload":{"tag":"h3","lines":"33,34"}}],"payload":{"tag":"h2","lines":"17,18"}}]},{"colorFreezeLevel":2})</script> </body> </html>
最新发布
06-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值