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