在《深入浅出学习Struts1框架(一):一个简单mvc模式代码示例开始》和《深入浅出学习Struts1框架(二):重构MVC模式代码中跳转路径和业务逻辑》文章中已经说了分层和mvc模式的区别,和一些为了去掉mvc模式代码中的TestServlet类中的if-else。因为if-else在程序代码中是相对不稳定的,所以通过去掉if-else来引入对struts框架的学习。
在《深入浅出学习Struts1框架(二):重构MVC模式代码中跳转路径和业务逻辑》中我们已经抽象出来了一个接口和四个实现类,主要封装了业务处理和页面跳转路径字符串放回,以便利用多肽来重构了TestServlet代码(具体见博客)。
在上两篇博客我们遗留了两个问题,一是if-else,二是字符串太多。 今天这篇博客就要解决这两个问题。
上一篇TestServlet重构后的代码:
package com.cjq.servlet;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestURI=request.getRequestURI();
System.out.println("request="+requestURI);
String path=requestURI.substring(requestURI.indexOf("/",1),requestURI.indexOf("."));
System.out.println("path="+path);
Action action=null;
if("/servlet/delUser".equals(path)){
action=new DelUserAction();
}else if("/servlet/addUser".equals(path)){
action=new AddUserAction();
}else if("/servlet/modifyUser".equals(path)){
action=new ModifyUserAction();
}else if("/servlet/queryUser".equals(path)){
action=new QueryUserAction();
}else{
throw new RuntimeException("请求失败");
}
String forward=null;
try{
forward=action.execute(request, response);
}catch(Exception e){
e.printStackTrace();
}
request.getRequestDispatcher(forward).forward(request, response);
}
}
解决字符串问题,当然就要用到配置文件了,用到配置文件就要有用来读取配置文件的相关的类和方法,这里就用dom4j中的类来读取配置文件,这里的配置文件的书写是有点逻辑上的难度的。
我们来看TestServlet中的代码,我们要在这个testservlet中实现读取配置文件和path比较,还有利用多肽实例化相应的实现类,最后通过实例化的实现类的方法来返回跳转路径,最终跳转到相应的页面。
所以我们的配置文件就要不仅配上testservlet中出现的字符串,还要配置相应的Action接口的实现类(我们可以利用反射来实例化该类的对象,进而使用这个类的所有属性和方法),另外还有跳转路径字符串。这样我们的配置文件就变成了如下代码所示:
<?xml version="1.0" encoding="UTF-8"?>
<action-config>
<action path="/servlet/delUser" type="com.cjq.servlet.DelUserAction">
<forward name="success">/del_success.jsp</forward>
<forward name="error">/del_error.jsp</forward>
</action>
<action path="/servlet/addUser" type="com.cjq.servlet.AddUserAction">
<forward name="success">/add_success.jsp</forward>
<forward name="error">/add_error.jsp</forward>
</action>
<action path="/servlet/modifyUser" type="com.cjq.servlet.ModifyUserAction">
<forward name="success">/modify_success.jsp</forward>
<forward name="error">/modify_error.jsp</forward>
</action>
<action path="/servlet/queryUser" type="com.cjq.servlet.QueryUserAction">
<forward name="success">/query_success.jsp</forward>
<forward name="error">/query_error.jsp</forward>
</action>
</action-config>
我们有了配置文件之后就要想法通过相关类读取,并且实现相应的功能。所以这里用dom4j来读取完成。其实如果能把这个逻辑捋顺之后就能发现,其实懂我们利用dom4j读取完配置文件的时候,我们是取得的是一个配套的匹配路径字符串、相应业务逻辑类还有处理业务逻辑之后跳转页面路径字符串。这样我们就能直截了当的去掉了if-else。(这里可能逻辑上会出现一些困难,但是看到下面的重构之后的testservlet中的代码和读取配置文件之后的代码就会一目了然)。
现在等待解决的问题就是我们要把从配置文件取得的一整套内容放到那里,当然这是毋庸置疑的要放到类中。所以我们就建立一个ActionMapping类来放我们的那一整套内容。
ActionMapping中的代码如下:
package com.cjq.servlet;
import java.util.Map;
public class ActionMapping {
private String path;
private Object type;
private Map forwardMap;
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public Object getType() {
return type;
}
public void setType(Object type) {
this.type = type;
}
public Map getForwardMap() {
return forwardMap;
}
public void setForwardMap(Map forwardMap) {
this.forwardMap = forwardMap;
}
}
现在ActionMapping类已经有了,剩下的工作就是要利用dom4j来读取配置文件类,具体代码如下:
package com.cjq.servlet;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class XmlConfigReader {
private static XmlConfigReader instance=new XmlConfigReader();
ActionMapping actionMapping=new ActionMapping();
private Document doc;
private Map actionMap=new HashMap();
private XmlConfigReader(){
try {
SAXReader reader=new SAXReader();
InputStream in=Thread.currentThread().getContextClassLoader().getResourceAsStream("action_config.xml");
doc=reader.read(in);
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public ActionMapping getActionMapping(String path){
synchronized(this){
Object type=null;
/*if(action.containsKey(path)){
type=action.get(path);
}*/
Element eltAction = (Element)doc.selectObject("//action[@path=\"" + path + "\"]");
try{
type=Class.forName(eltAction.attributeValue("type")).newInstance();
}catch(Exception e){
e.printStackTrace();
}
Element eltForwards = eltAction.element("forward");
for (Iterator iter = eltForwards.elementIterator(); iter.hasNext();) {
Element eltForward = (Element) iter.next();
actionMap.put( eltForward.attributeValue("name"),eltForward.getTextTrim());
}
actionMapping.setPath(path);
actionMapping.setType(type);
actionMapping.setForwardMap(actionMap);
return actionMapping;
}
}
public static synchronized XmlConfigReader getInstance(){
return instance;
}
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ActionMapping actionMapping=XmlConfigReader.getInstance().getActionMapping("/servlet/delUser");
System.out.println(actionMapping.getPath());
System.out.println(actionMapping.getType());
System.out.println(actionMapping.getForwardMap().toString());
}
}
我们通过返回ActionMapping来动态创建出action相应的实现类,进而完成业务逻辑和页面跳转,重构之后的TestServlet代码如下:
package com.cjq.servlet;
import java.io.IOException;
import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class TestServlet extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String requestURI=request.getRequestURI();
System.out.println("request="+requestURI);
String path=requestURI.substring(requestURI.indexOf("/",1),requestURI.indexOf("."));
System.out.println("path="+path);
String forward="";
ActionMapping actionMapping=XmlConfigReader.getInstance().getActionMapping(path);
Action action=(Action)actionMapping.getType();
try {
forward=action.execute(request, response);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
request.getRequestDispatcher(forward).forward(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request,response);
}
}
我们可以清晰的看到if-else已经没有了,字符串也已经没有了。通过这篇文章对if-else还有字符串问题的解决,又一次重构了testservlet代码,程序相对灵活许多。通过这一次的重构,我们已经看到了struts框架的雏形,下一篇文章就真正开始了struts框架的学习。
下一篇引子:
其实框架就是对程序的高度封装,我们经历了这三篇文章之后,一步一步重构,一步一步封装,逐步向框架靠拢,其实框架没有什么难的,其实没有学习框架之前感觉挺神秘,其实如果一步一步来研究之后发现框架就是封装的高度化,分层的高度化。
下一篇文章《深入浅出学习struts1框架(四):从MVC模式代码认识struts框架》就借助刚刚完成的实例来简单认识struts框架,看看真正的struts框架和我们这个mvc小实例有什么相同和相异之处。