`
7upCat
  • 浏览: 16239 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
最近访客 更多访客>>
社区版块
存档分类
最新评论

degister In Action (关于struts框架中的xml解析) part3

阅读更多

CH4.  分析 Struts中的struts-config.xml解析过程

写在最前面:说实在的,这部分会有点难懂,我自己也觉得很困难来把它整个说清楚,涉及到的类和方法都太宠杂,只能尽力而为.

因为本文主要是讨论使用digester进行xml解析.所以不关注,整个ActionServlet 初始化的整个流程,我们把注意力集中在解析xml配置文件的那部分代码,但读者还是要对Servlet,和Struts的一些相关的知识有一定的了解,比如说Servlet的生命周期,Struts的配置文件中的元素组成结构以及各个标签的含意是什么.




4.1 introduction of  ActionServlet


    形象点说,ActionServlet是Struts世界中的接线员,负责接收所有的请求,并把这个请求,转发给合适的人(这里的人在Struts中就是Action类型的实例了.
        ActionServlet继承了,javax.servlet.http.HttpServlet.是个Servlet当然也就遵循Servlet的生命周期.
  
   我们这里复习一下Servlet的生命周期
               1.Servlet实例被创建.
               2. 调用init(ServletConfig config )方法.(在HttpServlet中实际上在init(ServletConfig config ) 中调用了init()所以我们可以覆盖 init()方法来进行初例化)
               3.接受客户请求.调用service方法.(在HttpServelt中,会调用doGet和doPost)
               4. 调用destroy()方法.
               5.实例被销毁.


   接着,我们看一下,ActionServlet在生命周期都做了些什么事情呢?


   init()方法
                     

在struts源码中我增加了相关的注释,并删除了异常处理的代码,希望大家能更容易理解^^

 	 public void init() throws ServletException {

        	//初使化国际化相关的,MessageResource bundle
            initInternal();
            
            //初使化我们这个controller servlet的字符转换函数
            initOther();
            //初使化web.xml中的信息.
            initServlet();
        //将ActionServlet实例以Globals.ACTION_SERVLET_KEY为key存在ServletContext对象中
            getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
            initModuleConfigFactory();
            // Initialize modules as needed
            
            //初使化,ModuleConfig这个moduleConfig相当重要,封装了所有的,struts-config.xml中的信息.
            //也就是我们解析struts-config.xml中的root元素了.一会我们将来看一下initModuleConfig中都做了些什么事情.
            ModuleConfig moduleConfig = initModuleConfig("", config);
            
             
            //通过moduleConfig中的配置文件信息,创建MessageResource对象.
            initModuleMessageResources(moduleConfig);
            //通过moduleConfig中的配置文件信息,创建DataSource对象.
            initModuleDataSources(moduleConfig);
            //通过moduleConfig中的配置文件信息,创建
            initModulePlugIns(moduleConfig);
            //将配置文件中的各个对象,设置成已配置状态.
            moduleConfig.freeze();

Enumeration names = getServletConfig().getInitParameterNames();
            
            //通过web.xml的中参数配置,得到其它的Struts的配置文件并以上面的方式对,moduleConfig进行同样的配置
            while (names.hasMoreElements()) {
                String name = (String) names.nextElement();
                if (!name.startsWith("config/")) {
                    continue;
                }
                String prefix = name.substring(6);
                moduleConfig = initModuleConfig
                    (prefix, getServletConfig().getInitParameter(name));
                initModuleMessageResources(moduleConfig);
                initModuleDataSources(moduleConfig);
                initModulePlugIns(moduleConfig);
                moduleConfig.freeze();
            }
    
            this.initModulePrefixes(this.getServletContext());
            this.destroyConfigDigester();
      
    }

由上面的代码可以看出,在init方法中,Struts对很的信息进行了初使化,并使用,
ModuleConfig moduleConfig = initModuleConfig("", config)方法得到了struts-config.xml中的信息,所以解析配置文件的具体的方法也就是在这个方法里面完成的了.我们将在一会详细讲解一下这个方法的代码.


doGet  以及  doPost方法.
将请求委托给
process(request, response);方法.
我们再来看一下process方法又做了些什么呢?
 protected void process(HttpServletRequest request, HttpServletResponse response)
        throws IOException, ServletException {

        ModuleUtils.getInstance().selectModule(request, getServletContext());
        ModuleConfig config = getModuleConfig(request);
            
        //通过config对象来得来一个RequestProcessor对象(第一次请求的时候,为空的,).
        RequestProcessor processor = getProcessorForModule(config);
        if (processor == null) {
          //如果还没有配置一个RequestProcessor对象,就会通过,config对象来生成一个,默认就为org.apache.struts.action.RequestProcessor可以被扩展.
           processor = getRequestProcessor(config);
        }
        /**
            通过processor来转发处理请求.(无非是通过请求的路径,以及config对象来生成
              ActionMapping BeanForm 以及,具体的Action实例,和 ActionForward)
          然后调用得到的Action bean的execute方法
         */
        processor.process(request, response);
    }

所有的请求最终都是通过,ActionServelt 的process方法中被RequestProcessor来处理的,
RequestProcessor通过ModuleConfig对象里的信息,以前客户端的请求的路径,分别得到,在配置文件中所配置的,Action  bean的实例, ActionMapping的信息,ActionForward的信息,以及,可以所配置的FormBean的信息.然后调用  Action   bean 实例的execute方法来完成整个请求.


最后再来看destory方法

public void destroy() {

    
    /**
     * 和 init()方 法 相 对 应 ,做 了 相 关 的 清 理 工 作 ,具 体 的 ,读 者 可 以 参 见 init()方 法 
     */
        if (log.isDebugEnabled()) {
            log.debug(internal.getMessage("finalizing"));
        }

        destroyModules();
        destroyInternal();
        getServletContext().removeAttribute(Globals.ACTION_SERVLET_KEY);

        // Release our LogFactory and Log instances (if any)
        ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
        if (classLoader == null) {
            classLoader = ActionServlet.class.getClassLoader();
        }
  
            LogFactory.release(classLoader);
   

        PropertyUtils.clearDescriptors();

    }

这一节,我们详细的介绍了ActionServlet的生命周期中各个阶段所做的事情.下一节,我们将专注于,initModuleConfig方法,看一下struts只解析xml文件的具体过程.





4.2   concentrate   on the method of    initModuleConfig 

上一节,我们讲了ActionServlet的生命周期,其中有一个方法进入了我们的视野.
  ModuleConfig moduleConfig = initModuleConfig("", config) ,在这一节中我们将要仔细分析这个方法的源代码,
当然为了更容易理解,我将删除掉struts中源码的一部分,只保留核心那部分代码..

 protected ModuleConfig initModuleConfig(String prefix, String paths)
        throws ServletException {

        // Parse the configuration for this module
        ModuleConfigFactory factoryObject = ModuleConfigFactory.createFactory();
        
       
        /**
        通过prefix的值来创建ModuleConfig对象
        这里的prefix的含意是,模块名称,使用过struts的朋友可能有这个印象,在struts中,如果配置多个配置文件,是要在.
        web.xml中指定的路径是 config/name/***.xml 这里的name就是prefix了.^^
           <init-param>
            <param-name>config/name</param-name>
            <param-value>/WEB-INF/struts-config.xml</param-value>
        </init-param>
         如这个web.xml中的配置中的, name/即为模块的prefix
         */
        ModuleConfig config = factoryObject.createModuleConfig(prefix);

        // Configure the Digester instance we will use
        //初使化并,得到Digester对象(在初使化的过程中一定会添加各种监听,我们一会将看一下初使化的具体代码)
        Digester digester = initConfigDigester();


  // Process each specified resource path
        //配 置 文 件 的 路 径 名 的 字 符 串 ,可 以 指 定 多 个 配 置 文 件 的 路 径 ,以  ','  间 隔 .
        //这 里 将 迭 代 解 析 每 一 个 路 径 下 的 配 置 文 件 

        while (paths.length() > 0) {
            digester.push(config);
            String path = null;
            int comma = paths.indexOf(',');
            if (comma >= 0) {
                path = paths.substring(0, comma).trim();
                paths = paths.substring(comma + 1);
            } else {
                path = paths.trim();
                paths = "";
            }

            if (path.length() < 1) {
                break;
            }


 //将 解 析 的 具 体 操 作 委 托 给 parseModuleConfigFile方 法 

            this.parseModuleConfigFile(digester, path);
        }



 //将 所 得 到 的 ModuleConfig对 象 以 org.apache.struts.action.MODULE为 key放 进  ServletContext对 象 中 去 
        //以 方 便 以 后 的 使 用 

        getServletContext().setAttribute(
            Globals.MODULE_KEY + config.getPrefix(),
            config);
        return config;
    }

  




整个方法中很明确,首先,通过,initConfigDigester();方法来得到一个,已经被设置了各种监听的Digester对象,然后将path路径下的xml 文件进行解析.那么,到底设置了哪些监听规则呢?我们来跟到initConfigDigester()方法中看一下.
(源码的版本依然被我阉割了,以便于大家理解.)

  protected Digester initConfigDigester() throws ServletException {
 
    	//是否已经存在解析好了的configDigester对象了,如果是直接返回.
        if (configDigester != null) {
            return (configDigester);
        }

        // Create a new Digester instance with standard capabilities
        //创建一个新的Digester对象,并设置参数
        configDigester = new Digester();
        //名字是否空间敏感.
        configDigester.setNamespaceAware(true);
        //设置是否验证XML文档有效
        configDigester.setValidating(this.isValidating());
        //设置是否使用上下文的ClassLoader(通过,Thread.currentThread().getContextClassLoader()得到的)来加载
        //为digester对象设置的监听类
        configDigester.setUseContextClassLoader(true);
        //为digester对象来填加监听类集合
        configDigester.addRuleSet(new ConfigRuleSet());
        // Return the completely configured Digester instance
        return (configDigester);
    }


从这个方法中,我们可以看到,具体的匹配规则是通过,一个叫ConfigRuleSet()的对象传进来的.
我们接着考查,到底这个ConfigRuleSet()是个啥鬼东西^^.
这个类大约有五百行大小,为了方便,我们在这里讲解,我们只截取其中的一部分,但这也足够大家理解了.








public class ConfigRuleSet extends RuleSetBase {

   public void addRuleInstances(Digester digester) {
    //步骤一
 digester.addRule
            ("struts-config/action-mappings",
             new SetActionMappingClassRule());
    //步骤二
        digester.addFactoryCreate
            ("struts-config/action-mappings/action",
             new ActionMappingFactory());
    //步骤三
        digester.addSetProperties
            ("struts-config/action-mappings/action");
    //步骤四
        digester.addSetNext
            ("struts-config/action-mappings/action",
             "addActionConfig",
             "org.apache.struts.config.ActionConfig");
     //步骤五
        digester.addSetProperty
            ("struts-config/action-mappings/action/set-property",
             "property", "value");

       }


final class SetActionMappingClassRule extends Rule {

    public  SetActionMappingClassRule() {
        super();
    }

    public void begin(String namespace, String name, Attributes attributes) throws Exception {
        String className = attributes.getValue("type");
        if (className != null) {
            ModuleConfig mc = (ModuleConfig) digester.peek();
            mc.setActionMappingClass(className);
        }
    }

}

final class ActionMappingFactory extends AbstractObjectCreationFactory {

    public Object createObject(Attributes attributes) {

        // Identify the name of the class to instantiate
       String className = attributes.getValue("className");
        if (className == null) {
            ModuleConfig mc = (ModuleConfig) digester.peek();
            className = mc.getActionMappingClass();
        }

        // Instantiate the new object and return it
        Object actionMapping = null;
        try {
            actionMapping =
                RequestUtils.applicationInstance(className);
        } catch (Exception e) {
            digester.getLogger().error(
                    "ActionMappingFactory.createObject: ", e);
        }

        return actionMapping;
    }

}

 }


下面我们就按照代码中注释的五个步骤一步一步分析.

步骤一.  digester.addRule
            ("struts-config/action-mappings",
             new SetActionMappingClassRule());
当解析的时候碰到<action-mappings>标签时,调用下面的方法

public void begin(String namespace, String name, Attributes attributes) throws Exception {
        String className = attributes.getValue("type");
        if (className != null) {
            ModuleConfig mc = (ModuleConfig) digester.peek();
            mc.setActionMappingClass(className);
        }
    }
我们看到它将取得<action-mappings >
标签中的type元素的值并把它设置给配置文件的Root元素ModuleConfig的actionMappingClass,这个域的值默认为
org.apache.struts.action.ActionMapping

步骤二,   digester.addFactoryCreate
            ("struts-config/action-mappings/action",
             new ActionMappingFactory());
当遇到<action>标签时,将使用ActionMappingFactory类的createObject方法来创建一个对象.

 public Object createObject(Attributes attributes) {

        // Identify the name of the class to instantiate
       String className = attributes.getValue("className");
        if (className == null) {
            ModuleConfig mc = (ModuleConfig) digester.peek();
            className = mc.getActionMappingClass();
        }

        // Instantiate the new object and return it
        Object actionMapping = null;
        try {
            actionMapping =
                RequestUtils.applicationInstance(className);
        } catch (Exception e) {
            digester.getLogger().error(
                    "ActionMappingFactory.createObject: ", e);
        }

        return actionMapping;
    }

通过,<action > 标签中的className属性来创建对象,当这个className没有指定时,使用默认的
org.apache.struts.action.ActionMapping类来创建对象.




步骤三.    digester.addSetProperties
            ("struts-config/action-mappings/action");
当碰到<action>标签时,使用<action >标签中的属性来设置  ActionMapping对象.
主要包括path, type,scope,validate等 这样我们就可以看到,所有的关于bean的配置信息实际都是放在了
ActionMapping对象中了.

步骤四. digester.addSetNext
            ("struts-config/action-mappings/action",
             "addActionConfig",
             "org.apache.struts.config.ActionConfig");
我们知道,整个xml文件的解析的项层对象是一个ModuleConfig对象,那么上面的这个方法的意思就是说,
当碰到<action>标签的时候,我们把生成 的ActionMapping对象,通过调用ModuleConfig对象的addActionConfig方法设置给root对象.
看一下,addActionConfig方法的具体实现.
  public void addActionConfig(ActionConfig config) {

        if (configured) {
            throw new IllegalStateException("Configuration is frozen");
        }
        config.setModuleConfig(this);

      //通过ActionMapping中的path为key,以ActionMapping对象为值放入actionConfigs这个Map中
        actionConfigs.put(config.getPath(), config);
        actionConfigList.add(config);

    }

步骤五  digester.addSetProperty
            ("struts-config/action-mappings/action/set-property",
             "property", "value");

       }
要使用action-mappings/action/set-property,需要编写ActionMapping子类,并给action-mappings 标签加上type属性,属性值就是我们编写的字类。这样当action的execute享有客户操作时将得到我们编写的ActionMapping事例。

我们编写的ActionMapping要具有action-mappings/action/set-property所设置的所有属性。如果action-mappings/action拥有set-property子标签,那么execute方法获得的ActionMapping事例就拥有set-property所设置的属性值。
一般来讲我们都不会自己来扩展ActionMapping类,也就使得这个方法不是特别重要.


4.3 小节
   通过上一节,我们分析了ActionMapping的解析过程,当然在struts配置文件中,其它部分的解析方式实际上都是同这个类似的,大家完全可以举一反三的去看它的源代码.(重复性劳动没有啥意义SO.)
分享到:
评论
3 楼 harrison2010 2012-05-15  
我了个去,继续不?
2 楼 7upCat 2009-02-22  
我这篇文章主要讲的是关于Digester的使用,而关于struts源码分析部分,只是用来示例Digester实际应用中是如何使用的。所以,你别指望我根据这个来把Struts讲的很清楚,Digester是Struts用来解析xml文件的一个包。。


为什么看开源框架源码?
1. 成功的开源框架的共同待性:体现了某种先进的思想,并给出了一个比较高质量的实现。
   而我们主要是要先明白思想。
   看,STRUTS, 关注它是如何实现的MVC分离。
   看,SPRING,我们要明白,AOP,IOC。
2.成功的开源框架,住住都有比较好的代码习惯(包含命名,注释,类的责任划分)。这点值得我们学习的。
3.成功的开源框架,一般都使用了大量的设计模式,这也是你学习设计模式的一种实战的教科书。
4.做为程序员,是应该有点探取精神的,我们做程序员,不是做代码搬运工人,你应该对你使用的框架有一定的了解,并能根据你的业务需要对它进行定制 。



   
  
1 楼 iovy 2009-02-22  
LZ,开源框架的代码不是这么看的,你这叫只见一叶,不见森林。

相关推荐

    精通Struts:基于MVC (part 3)共分5个part

    ### 精通Struts:基于MVC (Part 3) #### Struts框架概述 Struts是一个开源的Java Web应用程序框架,它遵循Model-View-Controller(MVC)设计模式。MVC是一种软件架构模式,它将应用程序分为三个主要部分:模型、...

    精通Struts:基于MVC (part 2)共分5个part

    本文将围绕标题“精通Struts:基于MVC (part 2)”进行展开,并结合描述中的内容:“这是一本关于Struts很好的书,书中全面地介绍了Struts框架在J2EE中的完美运用...”,来生成相关的知识点。 ### Struts框架简介 ...

    精通Struts:基于MVC (part 4)

    2. **请求被ActionServlet接收**:ActionServlet是Struts框架的核心控制器,它根据配置文件struts-config.xml中的映射找到对应的Action。 3. **执行Action**:Action是具体处理用户请求的类,它负责调用业务逻辑层的...

    精通Struts:基于MVC (part 1)共分5个part

    3. ActionServlet根据配置文件struts-config.xml中的映射关系找到对应的Action。 4. ActionServlet创建一个ActionForm对象,并将请求参数填充到ActionForm中。 5. ActionServlet将ActionForm对象传递给Action对象...

    Struts2学习资源Result part1.rar

    Result类型是通过配置在Action类的execute方法返回值或者struts.xml配置文件中定义的。默认情况下,如果未明确指定Result类型,那么Struts2会默认使用"dispatcher",它将请求分发到一个JSP页面。然而,Struts2提供了...

    精通Struts:基于MVC (part 5)共分5个part

    Struts框架使用struts-config.xml文件进行配置,这个文件定义了Action的映射、Form Bean的声明等重要的配置信息。例如,可以配置Action的路径、方法名等。 ```xml &lt;action path="/welcome" type=...

    Struts2视频学习代码part2

    在struts.xml配置文件中,你可以为每个Action配置不同的Result,指定不同情况下应返回的视图。 3. **OGNL(Object-Graph Navigation Language)**:OGNL是Struts2中用于数据绑定和表达式语言,它允许在Action与视图...

    struts2 apps part2

    在"struts2 apps part2"这个主题中,我们将会深入探讨Struts2框架的更多特性和应用,以及如何利用它来开发更复杂的web应用程序。 1. **Struts2框架概述**: Struts2是基于MVC(Model-View-Controller)设计模式的...

    Struts开发入门与项目实践part2实例源码

    本实例源码集是“Struts开发入门与项目实践part2”的配套资源,旨在帮助初学者通过实际操作掌握Struts框架的核心概念和使用技巧。 在Struts框架中,核心概念包括: 1. **Action类**:Action类是业务逻辑的载体,...

    Struts2.part4

    Struts2是Java EE开发中的一个非常重要的框架,它主要用于构建Web应用程序,尤其在企业级应用中广泛应用。作为轻量级的解决方案,Struts2提供了一种组织良好、可扩展的架构,帮助开发者处理控制器逻辑、视图呈现以及...

    STRUTS-2.3.1.2-PART1

    通过这些示例,开发者可以学习到如何配置Struts 2的XML配置文件(struts.xml),创建Action类,定义视图页面,以及如何利用拦截器进行业务逻辑处理。 需要注意的是,由于文件大小限制,此压缩包被分为两部分上传,...

    struts-2.3.8-all-part1

    总之,"struts-2.3.8-all-part1"包含了Struts 2框架的完整构建,对于开发者来说是一个宝贵的资源,无论是初学者还是经验丰富的开发者,都能从中学习到很多关于MVC架构、Web开发最佳实践以及如何有效利用框架提升开发...

    struts2的pdf教程

    在Struts2框架中,核心组件包括Action类、配置文件(struts.xml)、拦截器(Interceptors)以及结果类型(Result)。Action类是业务逻辑的载体,用户请求会映射到特定的Action,然后执行相应的业务逻辑。配置文件...

    Struts2上传和下载Action配置

    在配置文件(struts.xml)中,我们需要为这两个Action定义对应的映射: ```xml &lt;struts&gt; &lt;package name="default" namespace="/" extends="struts-default"&gt; &lt;action name="upload" class=...

    struts-2.3.1.2-part2.rar

    总的来说,Struts 2.3.1.2-part2.rar这个压缩包包含了SSH框架中Struts 2的升级版本,其文档和源代码对于开发者来说是宝贵的资源,可以帮助他们更好地掌握SSH框架的使用,提高开发效率,构建高效、稳定的企业级Web...

    struts-2.3.8-all-part2

    5. **配置文件**:通常为struts.xml,用于定义Action、结果类型、拦截器栈等,是整个框架的配置中心。 6. **表达式语言(OGNL)**:Struts2使用OGNL作为默认的表达式语言,允许在Action、视图和配置文件中方便地...

    struts2.part2

    在“轻量级应用Java EE企业实战”系列中,"struts2.part2"这部分可能涉及了更深入的Struts2使用技巧,如自定义拦截器的实现、Action的分层设计、动态结果集的配置以及与Ajax的集成等内容。学习这部分内容将有助于...

    struts支持的上传

    Struts通过使用Servlet API中的`Part`接口(在Servlet 3.0及以上版本中引入)或者`HttpServletRequest`的`getParts()`方法来处理文件上传。它依赖于第三方库如Apache Commons FileUpload,这个库提供了处理多部分...

    Struts2的视频学习代码

    "Struts2-part01"可能包含的是该系列学习的第一部分内容,可能涵盖了Struts2的基础概念、环境搭建、Action的创建和执行、OGNL的基本使用等内容。通过学习这部分代码,开发者能够初步掌握Struts2的基本操作和工作原理...

Global site tag (gtag.js) - Google Analytics