简略的Struts1.0的实现

简单的Struts1.0的实现
  前一段时间写了一个仿Struts的框架,就是一个简单内核。我看过很多关于Struts的书,大多是一些应用方面的书,所以很多JAVA爱好者不能满足对于应用方面的知识,我们需要了解它的原理,想知道它是怎么来的(借用一下赵本山的话)。
简单的说一下原理,第一步,写一个自己的Servlet,在里面解析struts-config.xml;第二步,通过解析后的struts-config.xml找要访问的Action,再把请求的参数映射到Form里。就这么简单,它就是被包装后的Servlet。

类图
简略的Struts1.0的实现

1、web.xml与同普通Struts的配置一样
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
	
	<display-name>SWStruts</display-name>
	<welcome-file-list>
		<welcome-file>/WEB-INF/index.jsp</welcome-file>
	</welcome-file-list>
	<servlet>
		<servlet-name>action</servlet-name>
		<servlet-class>com.swstruts.action.ActionServlet</servlet-class>
		<init-param>
			<param-name>config</param-name>
			<param-value>/WEB-INF/struts-config.xml</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>

com.swstruts.action.ActionServlet就是我的核心Servlet。

2、ActionServlet
  其实它就是普通的Servlet。通过上面web.xml的映射,只要是.do的访问都会先能过这个Servlet过滤到想要访问的Action里。因为这个Servlet的load-on-startup设置为0,所以Tomcat启动时,会执行init方法。ConfigInit类就是我的struts-config.xml解析器。
package com.swstruts.action;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ActionServlet extends HttpServlet {
	
	protected static String config = "/WEB-INF/struts-config.xml";
	private Processor processor = new Processor();

	public void init() throws ServletException {
		initialize();
		ConfigInit.init(config);
	}

	private void initialize() {
		try {
			config = getServletContext().getRealPath("/")
					+ getInitParameter("config");

		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void destroy() {
	}

	public void doGet(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		processor.process(request, response);
	}

	public void doPost(HttpServletRequest request, HttpServletResponse response)
			throws ServletException, IOException {
		processor.process(request, response);
	}
}


3、ConfigInit类
  struts-config.xml解析器。对于xml文件的解析,我这里用的dom4j,其实应该自己写一个,但这里的主题不是如何解析xml文件,把dom4j作为一个工具,让自己的代码简捷一点。把解析后的结点保存到MappingAction里,MappingAction是我封装的一个类。如何解析看代码。
package com.swstruts.action;
import java.io.File;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import com.swstruts.bean.MappingAction;

public class ConfigInit {

	public static void init(String config) {
		try {
			File f = new File(config);
			SAXReader reader = new SAXReader();
			Document doc = reader.read(f);
			Element root = doc.getRootElement();
			Element formmappings = (Element) root.element("form-beans");
			for (Iterator i = formmappings.elementIterator("form-bean"); i
					.hasNext();) {
				Element form = (Element) i.next();
				Mappings.forms.put((String) form.attributeValue("name"),
						(String) form.attributeValue("type"));
				Mappings.formInstances.put(
						(String) form.attributeValue("name"), Class.forName(
								(String) form.attributeValue("type"))
								.newInstance());
			}
			Element actionmappings = (Element) root.element("action-mappings");
			for (Iterator j = actionmappings.elementIterator("action"); j
					.hasNext();) {
				Element am = (Element) j.next();
				MappingAction action = new MappingAction();
				action.setParameter(am.attributeValue("parameter"));
				action.setName(am.attributeValue("name"));
				action.setType(am.attributeValue("type"));
				Map forward = new HashMap();
				for (Iterator k = am.elementIterator("forward"); k.hasNext();) {
					Element fo = (Element) k.next();
					forward.put((String) fo.attributeValue("name"), (String) fo
							.attributeValue("path"));
				}
				action.setForward(forward);
				Mappings.actions
						.put((String) am.attributeValue("path"), action);
			}
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

另附
struts-config.xml,与普通的Struts一样。(既然Struts前辈已经写好了定义,也没有必要别出心裁的创造一个struts-config.xml来证明这是我写的Struts。)
<?xml version="1.0" encoding="UTF-8"?>

<struts-config>
	<form-beans >
		<form-bean name="TestForm" type="example.form.TestForm"/>
	</form-beans>
	<action-mappings >
		
		<action path="/login"	
			parameter="showLogoutView"
			type="example.action.Test"
			name="TestForm">
			<forward name="success" path="/WEB-INF/success.jsp" />
			<forward name="fail" path="/WEB-INF/index.jsp" />
		</action>
		<action path="/testout"	
			parameter="showLogoutView"
			type="example.action.CsvOut"
			name="TestForm">
			<forward name="success" path="/WEB-INF/success.jsp" />
			<forward name="fail" path="/WEB-INF/index.jsp" />
		</action> 
	</action-mappings>
</struts-config>

Mappings类
public class Mappings {
	public static Map actions = new HashMap();
	public static Map forms = new HashMap();
	public static Map formInstances = new HashMap();
}
MappingAction类
package com.swstruts.bean;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MappingAction {
	private String parameter;
	private String name;
	private String type;
	private Map forward = new HashMap();

	public String getParameter() {
		return parameter;
	}

	public void setParameter(String parameter) {
		this.parameter = parameter;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getType() {
		return type;
	}

	public void setType(String type) {
		this.type = type;
	}

	public Map getForward() {
		return forward;
	}

	public void setForward(Map forward) {
		this.forward = forward;
	}
}


4、Processor核心,代码其实并不多,能解决问题就行。简单说一下原理,process方法就是控制分配的功能,找到*.do对应的Acrtion类,用newInstance()实例后,再调用execute实现具体机能。(原理就是继承)具体Acrtion类要继承Process接口,必须实现execute方法。setActionForm这个方法也很重要,这就是Struts区分Servlet的不同之处。把Parameter封装到Form中。具体实现看代码。
package com.swstruts.action;
import java.beans.IntrospectionException;
import java.lang.reflect.InvocationTargetException;
import javax.servlet.RequestDispatcher;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.swstruts.bean.MappingAction;
import com.swstruts.io.Process;

public class Processor {
	public void process(HttpServletRequest request, HttpServletResponse response) {
		String url = request.getServletPath();
		String mapping = url.split(".do")[0];
		MappingAction action = (MappingAction) Mappings.actions.get(mapping);
		if (action != null) {
			try {
				ActionForm form = setActionForm(request, action);
				Process process = (Process) Class.forName(action.getType())
						.newInstance();
				String result = process.execute(request, response, form);
				if (result != null) {
					String destination = (String) action.getForward().get(
							result);
					RequestDispatcher dispatcher = request
							.getRequestDispatcher(destination);
					dispatcher.forward(request, response);
				}
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (SecurityException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}
		} else {
			System.out.println("not find action!");
		}
	}

	private ActionForm setActionForm(HttpServletRequest request,
			MappingAction action) {

		String formpath = (String) Mappings.forms.get(action.getName());
		ActionForm actionform = null;
		if (formpath != null) {
			try {
				actionform = (ActionForm) Class.forName(formpath).newInstance();
				java.beans.BeanInfo info = java.beans.Introspector
						.getBeanInfo(actionform.getClass());
				java.beans.PropertyDescriptor pd[] = info
						.getPropertyDescriptors();
				for (int i = 0; i < pd.length; i++) {
					String fieldName = pd[i].getName();
					if (fieldName != null && !fieldName.equals("class")) {
						java.lang.reflect.Method writeMethod = pd[i]
								.getWriteMethod();
						String[] parValues = request
								.getParameterValues(fieldName);
						if (parValues == null) {
							writeMethod.invoke(actionform, "");
						} else {
							if (parValues.length == 1) {
								writeMethod.invoke(actionform, parValues[0]);
							} else {
								writeMethod.invoke(actionform, parValues);
							}
						}
					}
				}
			} catch (IntrospectionException e) {
				e.printStackTrace();
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			} catch (InvocationTargetException e) {
				e.printStackTrace();
			} catch (InstantiationException e) {
				e.printStackTrace();
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return actionform;
	}
}

另附
Process接口
package com.swstruts.io;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.swstruts.action.ActionForm;

public interface Process {
	public String execute(HttpServletRequest req, HttpServletResponse res,ActionForm form);
}

ActionForm类
package com.swstruts.action;
public class ActionForm implements java.io.Serializable {
}

原理里的内容就说到这里,写一个小例子来验证一下。
首先写一个login的jsp。
<html>
<head>
</head>
<body>
<form action="login.do" method="POST">
<p>
username:<input type="text" name="name"/>
</p>
<p>
password:<input type="password" name="password"/>
</p>
<p>
<input type="submit" value="login"/>
</p>
</form>
<%String message =(String)request.getAttribute("message");
 if(message == null){
 	message = "";
 }
%>
<%=message%>
<form action="testout.do" method="POST">
<p>
<input type="submit" value="textout"/>
</p>
</form>
</body>
</html>


其次login的Action
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.swstruts.action.ActionForm;
import com.swstruts.io.Process;
import example.form.TestForm;

public class Test implements Process {

	public String execute(HttpServletRequest request,
			HttpServletResponse response, ActionForm form) {
		if (form != null && form instanceof TestForm) {
			TestForm testform = (TestForm) form;
			String name = testform.getName();
			String password = testform.getPassword();
			if (name.equals("123") && password.equals("123")) {
				request.setAttribute("username", name);
				return "success";
			} else {
				request
						.setAttribute("message",
								"username or password is wrong");
				return "fail";
			}
		} else {
			return "fail";
		}

	}

}


再次login的Form
package example.form;
import com.swstruts.action.ActionForm;
public class TestForm extends ActionForm {
	private String name;
	private String password;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getPassword() {
		return password;
	}

	public void setPassword(String password) {
		this.password = password;
	}
}

最后login成功的jsp
<h1>success!</h1>
welcome <%=request.getAttribute("username")%>