Struts核心代码ActionServlet的运行 2篇

来源:互联网 发布:淘客返利软件 编辑:程序博客网 时间:2024/05/16 14:43

    早在3年前就开始用Struts了,因该也算用的比较早的一批人。菜鸟的时候也曾经看过别人写的Struts的运行机制,也看过部分代码,但都是草草了事,自己明白了大致方向就结束了。我经常问别人我们写的Action如UserAction是不是Serlvet的,是不是单例的。其实我也没看过实际代码,这样问别人的时候,内心也经常惶恐,惟恐怕从网上看见的资料有假。

     在公司担任技术经理也很长时间了,虽然公司希望自己在开发深度上有所突破,但是本身作为小公司,自己涉及的事情又太多了,时而搞搞bea的portal,时而出去吹吹牛,还经常带几个什么都不会的菜鸟做项目,这几年做的东西也不算少,eai soa eip bmp都有实际的项目经验,但是哪个都没有坚持一路走下去,今天领导要我给公司做java时间比较短的人讲讲struts,正长是个机会,可以把Struts的运行机制,自己好好看看。因为我觉得如果给大家讲hello world,估计大家也不会满意。说了半天废话(我真的很爱说废话诶)下面进入主题吧。

   Struts两大功能,我自己的体会,Struts一个很实用的功能是它的jsp tag,这个是个独立的模块,我们的页面都用他的tag ,虽然现在的extremecomponents也是不错。

  Struts的另一个主要的功能是业务数据流的传递   jsp---->form—》action,这个是整个Struts的核心流程。

  除了这两个主要的功能,剩下的都,都是些具体的基类和接口,方便我们自己的使用,从Struts1.0  1.1 1.2 1.3我们不难看出Struts的趋势也是越来越简单,越来越使用,也不是很一味的追求概念的真理化了。

    其实Struts的运行机制,只要看看ActionServlet就ok了,今天下午花了2个小时看了一边,也算略有收获吧,把原来的一些猜测的内容看清楚出处了。  如果你在次之前,对 单例和多线程不了解,我建议你 补充一下基础概念,因为所有的开源代码里这2个概念都是核心概念,代码也都是围绕着这2个概念去进行的。

    因为Struts也是jsp Servlet机制运行的,所以我们的入口点还先从Servlet的源头开始看,我们打开一个Struts的web项目。打开web.xml文件。 

 <servlet>
    <servlet-name>action</servlet-name>
    <servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
    <init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>
    <init-param>
      <param-name>debug</param-name>
      <param-value>3</param-value>
    </init-param>
    <init-param>
      <param-name>detail</param-name>
      <param-value>3</param-value>
    </init-param>
    <load-on-startup>0</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>action</servlet-name>
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

我们发现Struts需要在我们的项目里配置一个Servlet  ActionServlet它有几个初始化参数,和监听url里包含.do的url

我们打开ActionServlet类.先看看它的init()方法,因为这个是启动tomcat时就会执行的代码,看看它一开始做了些什么工作
    public void init() throws ServletException {

        initInternal();  
        initOther();
        initServlet();

        // Initialize modules as needed
        getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this);
        ModuleConfig moduleConfig = initModuleConfig("", config);
        initModuleMessageResources(moduleConfig);
        initModuleDataSources(moduleConfig);
        initModulePlugIns(moduleConfig);
        moduleConfig.freeze();
        Enumeration names = getServletConfig().getInitParameterNames();
        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();
        }
        destroyConfigDigester();

    }

 

里面都是些初始化的操作,其实不看代码,我们用脚都能想出来,它肯定是把struts-config.xml文件的内容装载到它封状好的配置信息类里,至于配置信息类,我就不介绍了,无非是些工厂模式或者单例的java类,

  我们先看 initInternal()

 internal = MessageResources.getMessageResources(internalName);  //不用具体看了,是把资源文件装载到类里   if (defaultFactory == null)
            defaultFactory = MessageResourcesFactory.createFactory();
        return defaultFactory.createResources(config);  工厂模式,基本上init里都是这么操作的

initOther() 这个方法读取web.xml里配置的

<init-param>
      <param-name>config</param-name>
      <param-value>/WEB-INF/struts-config.xml</param-value>
    </init-param>

并把这个属性值方法actionSerlvet的 protected String config = "/WEB-INF/struts-config.xml";里,因为servlet是单例的,这个属性也就理所当然的成为全局变量了,其实不是特殊位置,没必要在web.xml里配置了,因为这个属性 有默认值。  initOther() 方法对里convertNull也做了处理,如果你在web,xml 配置的话,它就会给beanutil注册默认转化信息。

initServlet() 也是把web.xml里的信息初始化了一下,放到 getServletContext()中了。对整个流程没影响。

getServletContext().setAttribute(Globals.ACTION_SERVLET_KEY, this); 这句话,很有意思,自己把自己注册到

getServletContext().中,在struts中,经常看见这样的用法。

oduleConfig moduleConfig = initModuleConfig("", config);
        initModuleMessageResources(moduleConfig);
        initModuleDataSources(moduleConfig);
        initModulePlugIns(moduleConfig);
        moduleConfig.freeze();

读取struts-config.xml文件的内容,到类里

********************************************************************************************************************

********************************************************************************************************************

 Struct中ActionServlet的运行历程:  
   
  首先是init()方法  
  ================================  
  1,initInternal()  
  初始化国际语言包(目前好像只支持英文和日文)  
  2,initOther()  
  其他初始化  
  通过读取web.xml中<param-name>config</param-name>的值确定struts-config.xml文件的位置(即使web.xml中没有写config,也会有一个默认位置)  
  通过读取web.xml中<param-name>convertNull</param-name>的值决定是否向后兼容struts   1.0,默认不兼容  
  3,initServlet()  
  初始化ActionServlet的映射和标签库,还是通过读web.xml来进行的。  
  4,将自己保存在ServletContext中。  
  5,初始化模块配置工厂ModuleConfigFactory(大部分为struts1.2新加的功能,作用不明)  
   
  接下来是doGet()/doPost()方法  
  ================================  
  1, 对于Get和Post是使用相同的方法处理的  
  2, 通过解析request得到一个path,这个path决定了将来使用哪一个ActionMapping  
  3, 设置国际化,response的contentType和是否进行缓存  
  4, 这一步比较有意思了,ActionServlet提供了一个简单的返回boolean值的   processPreprocess()方法,可以让我们的子类(Action)选择是否覆盖,如果覆盖了该方法并返回false,则ActionServlet不会再对request进行预处理。  
  5, 根据第2步产生的path来获得相应的配置信息,也就是ActionMapping,保存在request对象里。如果没有找到对应的信息,会产生一个出错信息  
  6, 安全验证,看用户有没有执行本请求的权限  
  7, 如果再配置文件中声明了ActionForm,则创建ActionForm实例(当然,对于普通的ActionForm和动态Form的创建过程是不一样的)  
  8, 根据配置文件中的scope的不同,将ActionForm实例存在request或者session中。  
  9, 接下来是激动人心的一幕:将request流中的数据自动写入ActionForm中。在这里struct通过一套复杂的过程解析了request流,在这里普通表单和“multipart/form-data”复合表单(通常用于上传文件)作了不同的处理,最后应该是通过类映射完成数据写入。在进行这些操作之前,会调用ActionForm的reset方法。  
  10, 如果设置了验证属性,则会调用ActionForm的Validation方法,验证如果失败则尝试转到input页面,如果没有服务器自己产生一个出错页面  
  11, 获取Action实例,在这里struct使用了一个简单的缓存机制,使用了一个HashMap来保存所有的Action实例(自然是以类名为key),如果在HashMap中找不到当前需要的Action,则实例化一个,然后放入HashMap。  
  12, 这一步不太明白,似乎是监测配置文件中是否设置了forward或者include,如果设置了,则转到相应页面,并停止当前的处理。为何这样做,或者说forward和include属性是做什么的,还是不太名字噢,希望高人指点。  
  13, 终于到了这一步……执行Action实例的execute方法,并且获得返回的Forward,呵呵。  
  14, 最后跳转到Forward指向的页面,如果Forward为null则什么也不做。、  
原创粉丝点击