概述
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 = newHashMap<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 = newHashMap<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 = newActionMappings();//mappingsActionServlet类的成员变量
       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版本。