`

趣谈人与软件设计(struts1源码)

阅读更多

struts1源码解析:
得益于设计大师的精心设计,我觉得整个struts1框架完全像个人一样,只不过这个人只会简单的识别外界向他请求的命令;
他的整个运行机制完全和人的运行机制大同小异,下面我一一介绍这种大同小异:
人与外界的交流的大致过程就是这样的:
人只要是有生命的,那么他都会对外界对其的影响作出反映;(外界对其的影响可以概括为一切能够影响其状态的东西包括:别人与之交流,看到的一切以及感觉到的一切);
他是怎么作出反映的呢?
很简单,就是根据自己大脑里保存的各种信息,比如:生活常识,生活阅历以及经验还有各种科学知识等等;
比如:我们在走路的时候会注意观察一下自己脚下的路是否平坦,然后调整自己走路的姿势;为什么我们会对脚下的路是否平坦作出反映呢?
因为我们大脑里保存着这样一条信息:如果脚下的路不平坦,那么我们不调整走路姿势的话就会很容易摔倒,就会很惨!所以我们才作出相应的正确的反映;
可以这样说:我们所做的一切反映都是根据自己大脑里已经保存的信息来进行的,大脑的神经系统就是起到这样一个很重要的作用:对与各种刺激能够通过神经系统来找到正确的信息参考并顺利的
完成此次反映;
所以我们会很容易理解疯子的行为?其主要原因就是他们的神经系统出现了不同程度的损伤导致其不能对刺激作出正确的反映,因为他们不能够正确的获取到能够处理此次刺激的大脑中已存在的信息;
说多了!我的目的是说现在的软件在人类的精心设计下已经能够达到人类的这种行为反映模式;就比如最前面提到的struts1框架:
struts1框架在启动的时候会进行下面主要的操作:
 initInternal();
 initOther();
 initServlet();
 initChain();
 getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
            initModuleConfigFactory();

            // Initialize modules as needed
            ModuleConfig moduleConfig = initModuleConfig("", config);

            initModuleMessageResources(moduleConfig);
            initModulePlugIns(moduleConfig);
            initModuleFormBeans(moduleConfig);
            initModuleForwards(moduleConfig);
            initModuleExceptionConfigs(moduleConfig);
            initModuleActions(moduleConfig);
            moduleConfig.freeze();

            Enumeration names = getServletConfig().getInitParameterNames();

            while (names.hasMoreElements()) {
                String name = (String) names.nextElement();

                if (!name.startsWith(configPrefix)) {
                    continue;
                }

                String prefix = name.substring(configPrefixLength);

                moduleConfig =
                    initModuleConfig(prefix,
                        getServletConfig().getInitParameter(name));
                initModuleMessageResources(moduleConfig);
                initModulePlugIns(moduleConfig);
                initModuleFormBeans(moduleConfig);
                initModuleForwards(moduleConfig);
                initModuleExceptionConfigs(moduleConfig);
                initModuleActions(moduleConfig);
                moduleConfig.freeze();
            }

            this.initModulePrefixes(this.getServletContext());

            this.destroyConfigDigester();
        } catch (UnavailableException ex) {
            throw ex;
        } catch (Throwable t) {
            // The follow error message is not retrieved from internal message
            // resources as they may not have been able to have been
            // initialized
            log.error("Unable to initialize Struts ActionServlet due to an "
                + "unexpected exception or error thrown, so marking the "
                + "servlet as unavailable.  Most likely, this is due to an "
                + "incorrect or missing library dependency.", t);
            throw new UnavailableException(t.getMessage());
        }
  以上这些代码就相当于我们制造了一个人,并且我们已经为其组装了胳膊啊,腿啊等等对外界刺激作出反映的前提物质;此时他还只是个白痴,不能对外界的刺激作出相应的反映,这个时候即使你骂他他也不会用手打你;
  还有就是我们还为其的大脑
  输入了各种信息,这些信息能够指导他怎么正确的对外界刺激作出反映!此时就不能无辜骂他了,不然他会还击哦!因为我们为他输入了这样一个信息:如果有人骂我,我就打他!呵呵开玩笑!
  以上代码主要是做了如下的工作:
  另外需要特别说明一下:以上步骤创建的不仅仅人类的大脑还有很多的信息类似于人脑中存储的各种信息;一旦创建好之后里面的各种信息就不会变动了,这点也是软件与人类之间最大的差距:
  因为人类可以通过不停的学习来增加大脑中存储的信息,而软件(机器人)就不具备学习能力!具备学习能力的机器人在好莱坞有些科幻电影中出现过,不过对于人类来说是很可怕的呵呵!
  *************************************** initInternal();****************************************
  
  initInternal();主要是加载一个资源文件,这个文件里面包含了整个框架需要的信息文件,此信息文件只是用于框架内部处理各种异常,加载过程的提示信息;这点要比人类的大脑还要先进,
  因为如果你生病的话医生不是很容易准确的立马就知道你的病因,因为你没有记录你生病的原因,但是这个框架如果运行的过程出现了问题就会记录问题根源;而上面那个资源文件里面就包含了
  所有可能出现的问题描述信息。所以其因为描述就是内部信息,也就是这些问题是框架本身的有关各种信息描述,与你利用它编写的应用程序无关。
  这个方法的执行回涉及到以下几个主要的类:
  他们之间的关系如下图所以:
  现在我们要仔细的说一下MessageResources这个类的主要方法,也就是或许特定信息描述的方法:
  在此之前先简单的说一下国际资源文件:
  举个例子:好比一个人,他如果向周游世界的话他本应该学会很多语言,包括他所有游览的所有国家的所有语言:这样他的大脑里就有很多信息描述,并且是针对各个国家的;
  他与人打招呼的信息描述可能有:在中国:你好;在美国:Hello!等;
  国际网站也是如此,如果用户是在中国方位的话,用户请求的连接参数后面就会附加上这样一个参数:Local=CHINA,这样该框架就会知道这个用户是中国人,他会期待中文显示,于是返回给他的信息描述全部是中文的;
  因此MessageResources的子类PropertyMessageResources便包含了一个变量(protected HashMap locales = new HashMap();)用户保存此次加载的信息资源文件有几种类型的并把信息放进去;
  并具体实现了父类中的
  public String getMessage(Locale locale, String key) 这个方法,这个方法也是我们最为关心的方法;系统获取信息描述就是利用这个方法的!
  差点忘记了还有一个比较常用的方法: public String getMessage(Locale locale, String key, Object[] args):他的作用就是可以自己附上参数然后个性化信息描述;
  比如:这个信息描述是:XX你好!然后你的参数是{"赵宇飞"},那么实际显示的信息是“赵宇飞你好!”,信息描述里面还可以包含多个XX,后面的参数的顺序要和实际的XX位置保持一致!
  *************************************** initOther();****************************************
    此方法里面主要是执行了下面一句:
     value = getServletConfig().getInitParameter("config");
     设置框架默认模块(moduel)的配置文件路径;
     ***************************************  initServlet();****************************************
     此方法主要是读取ActionServlet在Web.xml中的配置并设置UrlPattern以及ServletName代码如下:
     this.servletName = getServletConfig().getServletName();

        // Prepare a Digester to scan the web application deployment descriptor
        Digester digester = new Digester();

        digester.push(this);
        digester.setNamespaceAware(true);
        digester.setValidating(false);

        // Register our local copy of the DTDs that we can find
        for (int i = 0; i < registrations.length; i += 2) {
            URL url = this.getClass().getResource(registrations[i + 1]);

            if (url != null) {
                digester.register(registrations[i], url.toString());
            }
        }

        // Configure the processing rules that we need
        digester.addCallMethod("web-app/servlet-mapping", "addServletMapping", 2);
        digester.addCallParam("web-app/servlet-mapping/servlet-name", 0);
        digester.addCallParam("web-app/servlet-mapping/url-pattern", 1);

        // Process the web application deployment descriptor
        if (log.isDebugEnabled()) {
            log.debug("Scanning web.xml for controller servlet mapping");
        }

        InputStream input =
            getServletContext().getResourceAsStream("/WEB-INF/web.xml");

        if (input == null) {
            log.error(internal.getMessage("configWebXml"));
            throw new ServletException(internal.getMessage("configWebXml"));
        }

        try {
            digester.parse(input);
        } catch (IOException e) {
            log.error(internal.getMessage("configWebXml"), e);
            throw new ServletException(e);
        } catch (SAXException e) {
            log.error(internal.getMessage("configWebXml"), e);
            throw new ServletException(e);
        } finally {
            try {
                input.close();
            } catch (IOException e) {
                log.error(internal.getMessage("configWebXml"), e);
                throw new ServletException(e);
            }
        }

        // Record a servlet context attribute (if appropriate)
        if (log.isDebugEnabled()) {
            log.debug("Mapping for servlet '" + servletName + "' = '"
                + servletMapping + "'");
        }

        if (servletMapping != null) {
            getServletContext().setAttribute(Globals.SERVLET_KEY, servletMapping);
        }
    }
 里面主要用到XML解析框架Degister很好的XML解析参数它能够根据自定义的rule来讲XML中的元素赋值到类中的变量,关于它的具体用法我已经写过有关的文章;
 从以上的代码中
 ***************************************  initChain();****************************************
 这个方法里面做的东西真的是奇妙,下面就详细的介绍它:
 首先它里面主要用到一个开源的工具类commons-chains-1.2.jar;这是个很有名的实现了典型的职责链设计模式的java开源工具JAR包;
 关于职责链设计模式很有意思;大家可能对于工厂里德生产线很熟悉;工人把一个东西放到传送带的起始端,然后传送带开始move,每到一个特定的地方就会自动有工人或者
 机器来对其进行加工,就这样直到那个东西随着传送带move到末端,产品也即加工完毕!
 软件中的职责链模式也和上述过程大体类似,比如:struts1中,ActionContext这个容易里包含了用户请求信息以及系统相关信息,然后作为参数传递给Chain(相当于放到了传送带上)
 ,经过chain中的各个Command依次处理(就相当于流水线上的不同工人或机器部件的处理)最终整个用户请求处理也就结束了。它具有极大地可扩展性,因为你可以很容易的向chain中增加新的命令
 用于处理自己感兴趣的内容;我心中能够想象到的一个情景就是:一个包含了各种信息的小球,在一个链条上运动,一到一个事前具体设置的位置,位于链条上该位置的东西并会根据需要更改小球里面的
 信息,就这样直到小球运行到链条的末端,整个小球需要更改的信息便会更改完毕!很有意思!
 下面是代码:
  try {
            String value;

            value = getServletConfig().getInitParameter("chainConfig");

            if (value != null) {
                chainConfig = value;
            }

            ConfigParser parser = new ConfigParser();
            List urls = splitAndResolvePaths(chainConfig);
            URL resource;

            for (Iterator i = urls.iterator(); i.hasNext();) {
                resource = (URL) i.next();
                log.info("Loading chain catalog from " + resource);
                parser.parse(resource);
            }
        } catch (Exception e) {
            log.error("Exception loading resources", e);
            throw new ServletException(e);
        }
  其中chainConfig默认的值是 "org/apache/struts/chain/chain-config.xml";
  里面配置了框架需要的所有Command,用来完成框架处理用户请求的需要的所有步骤;
  后面将会介绍ActionServlet是如何利用已经初始化好的职责链来处理用户请求的。
  ***************************************  Each Module Instantiation ****************************************
  接下来的代码就是根据Web.xml中的配置来进行相关Moduel的配置;
  其实Struts1中的moduel就是负责处理用户请求的一块块功能单元;在这些功能单元里面可以个性化用户处理流程,就是前面说的那些职责链,每个Moduel都可以自己定义职责链内容也就是那个文件;这样就要求我们在
  架构系统的时候要对整个系统的需求清楚然后将系统可能接收到的请求划分成一个个单独的模块,当然属于各个模块的用户请求处理过程是很类似的,这样才应该放到一个Module里面。
  接着我们看struts1框架是如何用Degister来读取模块对应的配置文件并将其中的各个节点信息准确无误的赋值到Module实体中的对应属性上面;
  看下关键代码:
     Enumeration names = getServletConfig().getInitParameterNames();
           获取所有的你配置的模块前缀名字枚举
            while (names.hasMoreElements()) {
                String name = (String) names.nextElement();

                if (!name.startsWith(configPrefix)) {
                    continue;
                }

                String prefix = name.substring(configPrefixLength);
    获取模块对应的名称,这个名称很重要,属于该模块的Action请求字符串里面就有这个名称,框架就是通过判断请求字符串里面的整个名称来决定采用哪个模块来对其进行相应;
               
                moduleConfig =initModuleConfig(prefix,getServletConfig().getInitParameter(name));
    接着就是解析指定的Module配置文件,这个方法是解析Moduel配置文件中主要的方法;下面进行详细介绍;
                initModuleMessageResources(moduleConfig);
                initModulePlugIns(moduleConfig);
                initModuleFormBeans(moduleConfig);
                initModuleForwards(moduleConfig);
                initModuleExceptionConfigs(moduleConfig);
                initModuleActions(moduleConfig);
                moduleConfig.freeze();
            }

            this.initModulePrefixes(this.getServletContext());

            this.destroyConfigDigester();
        } catch (UnavailableException ex) {
            throw ex;
        } catch (Throwable t) {
            // The follow error message is not retrieved from internal message
            // resources as they may not have been able to have been
            // initialized
            log.error("Unable to initialize Struts ActionServlet due to an "
                + "unexpected exception or error thrown, so marking the "
                + "servlet as unavailable.  Most likely, this is due to an "
                + "incorrect or missing library dependency.", t);
            throw new UnavailableException(t.getMessage());
        }
  *******************************************initModuleConfig(prefix,getServletConfig().getInitParameter(name));*****************************************************
  此方法接受两个参数:模块对应名称,模块对应的配置文件;
  下面这个方法会执行下面具体复杂的代码:
  其实我们也能想到:首先初始化那个XML解析工具类Digester,并为其设置特殊的解析规则:
   Digester digester = initConfigDigester();
   此方法又会执行:
      // Create a new Digester instance with standard capabilities
        configDigester = new Digester();
        configDigester.setNamespaceAware(true);
        configDigester.setValidating(this.isValidating());
        configDigester.setUseContextClassLoader(true);
        configDigester.addRuleSet(new ConfigRuleSet());
  这个类里面包含了所有的Moduel配置文件中节点的处理方式,感兴趣的自己可以去看看!
  至此,整个框架已经初始化完毕,下面就可以开始一多线程的方式来相应和处理用户请求了!
  用于处理用户请求的代码入口为:
  protected void process(HttpServletRequest request,
        HttpServletResponse response)
        throws IOException, ServletException {
        ModuleUtils.getInstance().selectModule(request, getServletContext());
        根据用户请求路劲来获得用户请求所属于的模块并将模块设置到request中,Key为:Globals.MODULE_KEY
        ModuleConfig config = getModuleConfig(request);
         获取模块;
        RequestProcessor processor = getProcessorForModule(config);
  ****************************getProcessorForModule具体执行代码start*************************************************
         /*
  private RequestProcessor getProcessorForModule(ModuleConfig config)
        String key = Globals.REQUEST_PROCESSOR_KEY + config.getPrefix();
        return (RequestProcessor) getServletContext().getAttribute(key);           
   
   */
   ****************************getProcessorForModule具体执行代码end*************************************************
  
        if (processor == null) {
            processor = getRequestProcessor(config);
    ****************************getRequestProcessor具体执行代码start*************************************************
   /*
    RequestProcessor processor = this.getProcessorForModule(config);

        if (processor == null) {
            try {
                processor =
                    (RequestProcessor) RequestUtils.applicationInstance(config.getControllerConfig()
                                                                              .getProcessorClass());
            } catch (Exception e) {
                throw new UnavailableException(
                    "Cannot initialize RequestProcessor of class "
                    + config.getControllerConfig().getProcessorClass() + ": "
                    + e);
            }

            processor.init(this, config);

            String key = Globals.REQUEST_PROCESSOR_KEY + config.getPrefix();

            getServletContext().setAttribute(key, processor);
        }

        return (processor);
  */
  ****************************getRequestProcessor具体执行代码end*************************************************
        }

        processor.process(request, response);
    }
 下面就是最最主要的部分了,用于用户请求的处理:
 这个处理过程是由RequestProcessor的子类完成的,它就是这个类:ComposableRequestProcessor;用户是可以自己个性化定义的,下面我们就拿框架中已存在的默认的来详细的介绍一下其整个处理流程:
 
  
   待续。。。。。。

分享到:
评论

相关推荐

    设计模式趣谈之我之见解

    在软件开发领域,设计模式是经验的结晶,是解决常见问题的最佳实践。本文将结合个人的理解,探讨设计模式的魅力,并以Java语言为例,提供一些实用的见解和教程。设计模式并非孤立存在,而是贯穿于整个软件开发过程,...

    三角形趣谈

    三角形趣谈

    趣谈Linux操作系统

    Linux的开放源码特性使其成为全球开发者共同参与和改进的项目,拥有丰富的软件生态和强大的社区支持。 Linux运维是IT领域中的一个重要角色,运维工程师负责安装、配置、监控、优化以及维护Linux服务器,确保系统的...

    014-趣谈网络协议014-趣谈网络协议

    网络协议的实现通常涉及软件和硬件的结合。操作系统内核包含网络协议栈,负责处理网络通信。硬件设备如路由器和交换机也有自己的协议实现,以转发数据包。 总的来说,“014-趣谈网络协议”可能涵盖了以上这些内容,...

    计算机发展史趣谈资料.pdf

    欧洲人发明的算筹与中国不尽相同,他们的算筹是根据“格子乘法”的原理制成。例如要计算1248× 456,可以先画一个矩形,然后把它分成3× 2 个小格子,在小格子边依次写下乘数、被乘数的各位数字,再用对角线把小格子...

    计算机网络技术基础-趣谈网络协议.md

    计算机网络技术基础-趣谈网络协议.md

    法国人美食趣谈.doc

    法国人美食趣谈.doc

    趣谈网络协议 pdf .zip

    25软件定义网络 26云中的网络安全 27云中的网络Qo 28云中网络的隔离GRE、VXLAN 29容器网络 30容器网络之Flannel 31容器网络之Calico 32RPC协议综述 33基于XML的SOAP协议 34基于JSON的RESTful接口协议 35二进制类RPC...

    趣谈数据结构(五).doc

    《趣谈数据结构(五)》探讨了计算机程序设计中的一个重要算法——递归。递归算法是一种较难理解和掌握的技巧,它通过过程自身调用来实现自我嵌套执行。递归执行的流程可以用图形化的方式来表示,就像图1所示,呈现...

    32种设计模式趣谈.doc

    ### 32种设计模式趣谈 #### 一、创建型模式 1. **Factory (工厂模式)** - **定义**: 工厂模式是一种创建型设计模式,它提供了创建对象的最佳方式。此模式的核心思想是将对象的创建与使用分离,即消费者只需知道...

    05-趣谈网络协议.epub

    05-趣谈网络协议

    趣谈前端lodash1

    趣谈前端lodash1 lodash 库是一个功能强大的 JavaScript 实用程序库,提供了很多实用的函数来处理数组、对象、函数和其他数据类型。在前端开发中,lodash 库是一个非常常用的工具,它可以帮助开发者快速高效地处理...

    [详细完整版]趣谈大数据.pdf

    趣谈大数据 一、大数据的初步理解 似乎一夜之间,大数据(Big Data)变成一个IT行业中最时髦的词汇。 首先,大数据不是什么完完全全的新生事物,Google的搜索服务就是一个典型的大数据运用,根据 客户的需求,Google...

    【教学设计课题】《京剧趣谈》(语文人教五四学制六上).doc

    【教学设计课题】《京剧趣谈》是一篇针对人教版五四学制六年级上册语文教材的教学设计,旨在引导学生了解和欣赏京剧这一中国传统文化的瑰宝。课文通过介绍京剧中的“马鞭”和“亮相”两种艺术表现形式,揭示了京剧...

    英语词源趣谈-英语学习好帮手

    英语词源学是一门深入研究英语词汇起源与发展的学科,它揭示了单词背后的历史故事、文化含义以及演变过程。在英语学习中,了解词源能够帮助我们更深刻地理解词义,记忆单词,并且增强语言的感知力。《英语词源趣谈》...

    部编版第十七周 数字趣谈.doc

    1. 计数原则:在解决数字趣谈中的计数问题时,我们通常采用实验探究法和分类统计法。实验探究法是指通过实际操作或模拟实验来找出规律,例如例题1中通过列举3的倍数来找到在特定范围内的3的倍数个数。分类统计法则是...

    教学京剧趣谈语文人教五四学制六上PPT课件.pptx

    马鞭并非真实的马,而是象征性的道具,它既展示了骑马人的姿态,又赋予演员无限的表演空间。通过“彻底”和“无比漂亮”的描述,可以看出马鞭的引入不仅解决了舞台上无法呈现真实马匹的问题,还增添了视觉美感和艺术...

    关于java23种设计模式的有趣见解

    设计模式是软件工程中的重要概念,旨在解决软件开发中常见的问题,提高代码的复用性和可维护性。以下是对给定文件中提及的部分设计模式的详细解释: ### 创建型模式 #### 1. FACTORY(工厂模式) 工厂模式是一种...

    教学京剧趣谈语文人教五四学制六上PPT教案.pptx

    第二自然段进一步讨论了虚拟道具在京剧中的运用,展示出虚拟与真实的巧妙结合。第三自然段则揭示了虚拟道具如何增添表演的自由度和美感。 在研读课文的过程中,学生被问及马鞭如何产生,为何说它是“彻底”且“无比...

    教学京剧趣谈语文人教五四学制六上.pptx

    教学京剧趣谈语文人教五四学制六上.pptx

Global site tag (gtag.js) - Google Analytics