前一段时间写了一个仿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")%>