概述
MVC是现如今广泛应用的设计模式,在软件行业的应用非常广泛。所谓的MVC,具体为Model,View,Controller。用户的请求发给控制器,控制器根据请求的来源调用相应的Model组件完成业务逻辑,控制器根据Model返回的信息调用相应的View显示给用户。struts是MVC设计模式的具体应用,在经过人们群众广泛的实践后,已经成为了应用MVC设计模式的代表之作。笔者在培训期间学到了struts的奥妙所在,现梳理一番,加深记忆,也分享出来,希望得到大家的指教。
Step1

雏形

控制器是MVC的灵魂,它决定了数据的来与去,既然是灵魂,那么这个控制权当且仅当交给一个组件(中央集权)来管理,在我们的框架中,这个控制器名为ActionServlet,这个类继承自 HttpServlet, 用来接受用户请求并返回响应,既然所有的请求都交由ActionServlet来处理,我们需要在web.xml文件中配置一下,

     < servlet >

        < servlet-name > ActionServlet </ servlet-name >

        < servlet-class >

           org.mystruts.action.ActionServlet

        </ servlet-class >

        < load-on-startup > 1 </ load-on-startup >

     </ servlet >

 

     < servlet-mapping >

        < servlet-name > ActionServlet </ servlet-name >

        < url-pattern > *.action </ url-pattern >

     </ servlet-mapping >

这段配置说明了所有的.action请求都交由ActionServlet来处理。我们需要抽象一个用来代表所有对请求进行处理的类,这个类我们定义为Action

public   abstract   class  Action {

     public   abstract  String execute(HttpServletRequest request,

           HttpServletResponse response)  throws  Exception;

}

ActionServlet 根据用户的请求调用Action的子类完成业务逻辑处理,ActionServlet本身不处理请求,而是委派给对应的Action来处理,那么ActionServlet怎么知道哪些请求对应哪些Action呢?根据配置文件是一个比较好的解决方案,那么是属性文件呢,还是XML?我们选择XML,理由很简单,XML配置灵活且易于扩展,看看我们的配置信息怎么写:

    < action-mappings >

        < action  path = "/test"  type = "test.TestAction" >

            < forward  name = "success"  path = "/1.jsp" redirect=”true|false”  />

        </ action >

     </ action-mappings >

aciton-mappings 是根元素,其中的每个action子元素代表了不同的请求调用哪个Action来处理,forward元素代表根据Action返回的信息决定调用哪个jsp组件完成显示。

在我们的ActionServlet中,我们需要根据这个配置文件分发请求,目前,XML的解析技术主要有Saxdom,但是他们的处理方式都比较底层,我们选用apache的开源组件commons-digester解析XML,根据这个组件的需要,我们需要编写和xml中元素对应的类,即一个元素对应一个类,类的属性对应元素的属性,我们的类为ActionMappings、ActionMapping和ActionForward,分别对应action-mappings元素、action元素和forward元素

ActionMappings

public   class  ActionMappings {

     private  Map<String, ActionMapping>  mappings  =  new HashMap<String, ActionMapping>();

 

     public   void  addActionMapping(ActionMapping mapping) {

        mappings .put(mapping.getPath(), mapping);

    }

 

     public  ActionMapping findActionMapping(String path) {

        return   mappings .get(path);

    }

}

 

ActionMapping

public   class  ActionMapping {

     private  String  path ;

     private  String  type ;

   

     private  Map<String, ActionForward>  forwards  =  new HashMap<String, ActionForward>();

 

     public   void  addActionForward(ActionForward forward) {

        forwards .put(forward.getName(), forward);

    }

 

     public  ActionForward findActionForward(String name) {

        return   forwards .get(name);

    }

 

     public  String getPath() {

        return   path ;

    }

 

     public   void  setPath(String path) {

        this . path  = path;

    }

 

     public  String getType() {

        return   type ;

    }

 

     public   void  setType(String type) {

        this . type  = type;

    }  

}

ActionForward

public   class  ActionForward {

     private  String  name ;

     private  String  path ;

     private   boolean   redirect  =  false ;

 

     public  String getName() {

        return   name ;

    }

 

     public   void  setName(String name) {

        this . name  = name;

    }

 

     public  String getPath() {

        return   path ;

    }

 

     public   void  setPath(String path) {

        this . path  = path;

    }

 

     public   boolean  isRedirect() {

        return   redirect ;

    }

 

     public   void  setRedirect( boolean  redirect) {

        this . redirect  = redirect;

    }

}

另外,我们需要在digester规定的规则文件中定义一些规则,

rule.xml

<? xml  version = '1.0'  encoding = 'UTF-8' ?>

<!--

     这个xml文件是由commons-digester定义用于告诉digester组件

     自定义的配置文件和配置对象之间的关系,commons-digester组件了解了这

     个关系后就可以将配置文件中的信息转换为配置对象

-->

< digester-rules >

     <!-- ActionMappings -->

     < pattern  value = "action-mappings" >

        < pattern  value = "action" >

            <!-- 每碰到一个action元素,就创建指定类的对象-->

            < object-create-rule

               classname = "org.mystruts.config.ActionMapping" />

            <!--

               对象创建后,调用指定的方法,

               将其加入它上一级元素所对应的对象

           -->

            < set-next-rule  methodname = "addActionMapping"  />

            <!--

               action元素的各个属性按照相同的名称

               赋值给刚刚创建的ActionMapping对象

           -->

            < set-properties-rule  />

            < pattern  value = "forward" >

               < object-create-rule

                  classname = "org.mystruts.config.ActionForward"  />

               < set-next-rule  methodname = "addActionForward" />

               < set-properties-rule  />

            </ pattern >

        </ pattern >

     </ pattern >

</ digester-rules >

在我们的ActionServlet中,我们只需调用digester组件的一些api就可以将xml文件的信息保存到内存中,代码为

       Digester digester = DigesterLoader.createDigester(ActionServlet. class

             .getClassLoader().getResource( "org/mystruts/action/rule.xml" ));

        mappings  =  new ActionMappings();//mappings ActionServlet类的成员变量

       digester.push( mappings );

        try  {

          digester.parse(ActionServlet. class .getClassLoader()

                  .getResourceAsStream( "mystruts.xml" ));

       }  catch  (IOException e) {

            throw   new  IOException(e.getMessage());

       }  catch  (SAXException e) {

            throw   new  SAXException(e.getMessage());

       }

actionServletservice方法中,我们先获得用户请求的地址,然后截取地址的一部分,到mappings对象中找对应的Action,由这个Action处理请求,ActionServlet根据Action返回的信息再到mapping中查找对应的ActionForward,根据redirect属性,决定是转发还是重定向。
至此,雏形已经产生。我们暂且命名为mystruts1.0版本。