很多人在刚开始学习struts的时候,都搞不清struts的执行流程和底层的原理,本人开始也是,但是通过深入的学习,也试着自己来写一个模拟struts的框架,下面是我自己写的一个模拟struts2的框架。
首先先写一个登陆页面:
login.jsp
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> <title>login</title> </head> <body> <form action="login.action" method="post"> 用户名:<input type="text" name="username"/><br/> 密 码:<input type="password" name="password"/><br/> <input type="submit" value="提交"/> </form> </body> </html>
然后我们编写一个Servlet来处理所有以.action结尾的请求,因为这是一个MVC框架,我们的请求action以及结果视图都应在配置文件中配置,以达到解耦的目的,配置文件mystruts.xml在后面给出:
在读取配置文件时,我们需要对读取的配置信息进行保存,所以我们需要创建保存配置文件的两个实体类ActionMapping和Result类,在这里我们还需要导入dom4j这个jar包以读取配置文件:
Result.java
package org.wp.action; /** * 定义Result * 保存配置的Result节点的信息 */ public final class Result { private String name ;//result的名称 private String url ; //result的跳转页面 private Boolean redirect ;//是否重定向 public Result(){} public Result(String name, String url, Boolean redirect) { this.name = name; this.url = url; this.redirect = redirect; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public Boolean getRedirect() { return redirect; } public void setRedirect(Boolean redirect) { this.redirect = redirect; } }
ActionMapping.java
package org.wp.action; import java.util.HashMap; import java.util.Map; /** * 定义ActionMapping * 保存配置文件中的信息 */ public final class ActionMapping { private String name ; //action名称 private String className ; //权限定类名 //action对应的Result private Map<String,Result> results = new HashMap<String, Result>(); public String getName() { return name; } public ActionMapping() { } public ActionMapping(String name, String className) { this.name = name; this.className = className; } public void setName(String name) { this.name = name; } public String getClassName() { return className; } public void setClassName(String className) { this.className = className; } public Map<String, Result> getResults() { return results; } public void setResults(Map<String, Result> results) { this.results = results; } }
ActionServlet.java
package org.wp.servlet; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; import java.util.Enumeration; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.wp.action.ActionMapping; import org.wp.action.Result; /** 核心控制器 拦截所有以.action结尾的请求 **/ public class ActionServlet extends HttpServlet { // mappings保存所有从配置文件mystruts.xml读取的信息 key:提交的请求名,value:请求名对应的action信息 private Map<String,ActionMapping> mappings = new HashMap<String, ActionMapping>(); @SuppressWarnings("unchecked") public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //获得表单提交的所有请求参数名 Enumeration<String> en = request.getParameterNames(); //获得请求的uri 如:/MyStruts/login.action String uri = request.getRequestURI(); //获得上下文路径 /MyStruts String contextPath = request.getContextPath(); //通过字符串截取获得请求的action名称 即:login String actionName = uri.substring(contextPath.length()+1,uri.lastIndexOf(".")); //通过请求的action名,获得对应的映射信息 ActionMapping mapping = mappings.get(actionName); String className = null ; try { //获得对应的 className = mapping.getClassName(); } catch (Exception e) { throw new RuntimeException("请求的action找不到"); } Object obj = instance(className); //获得该类的所有方法 Method[] methods = obj.getClass().getMethods(); setValue(en,methods,obj,request); Method m = null ; try { m = obj.getClass().getMethod("execute",null); String s = (String) m.invoke(obj, null); if(s!=null){ //取得结果试图 Result r = mapping.getResults().get(s); //判断是否是重定向 if(r.getRedirect()){ response.sendRedirect(request.getContextPath()+r.getUrl()); }else{ request.getRequestDispatcher(r.getUrl()).forward(request, response); } } } catch (Exception e) { e.printStackTrace(); } } /** * 通过反射为对应的Action中的属性赋值 * @param en * @param methods * @param obj * @param req */ private void setValue(Enumeration<String> en, Method[] methods,Object obj,HttpServletRequest req) { while (en.hasMoreElements()) { String name = en.nextElement(); String name_ = name.substring(0,1).toUpperCase().concat(name.substring(1)); for (Method m : methods) { if(m.getName().startsWith("set")){ String n = m.getName().substring(3); if(n.equals(name_)){ try { String value = req.getParameter(name); m.invoke(obj,value); } catch (Exception e) { e.printStackTrace(); } } } } } } /** * 通过反射初始化对应的处理类 * @param className * @return Object */ private Object instance(String className) { try { return Class.forName(className).newInstance(); } catch (Exception e) { e.printStackTrace(); } return null; } public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { this.doGet(request, response); } /** * 初始化ActionServlet时,就读取指定的配置文件 */ public void init(ServletConfig config) throws ServletException { //获得配置文件的名字 String configName = config.getInitParameter("configName"); String[] names = configName.split(","); for (String name : names) { readXML(name); } } /** * 读取指定的配置文件 * @param name */ @SuppressWarnings("unchecked") private void readXML(String name) { //加载配置文件 InputStream is = this.getClass().getClassLoader().getResourceAsStream(name); SAXReader read = new SAXReader(); try { //通过dom4j读取配置文件 Document document = read.read(is); //得到根元素 Element root = document.getRootElement(); //得到actions元素 Element actions = root.element("actions"); //迭代action元素 Iterator<Element> it = actions.elementIterator("action"); while(it.hasNext()){ Element action = it.next(); //封装action属性 ActionMapping mapping = new ActionMapping( action.attributeValue("name"),action.attributeValue("class")); //迭代result元素 Iterator<Element> results = action.elementIterator("result"); while(results.hasNext()){ Element result = results.next(); //封装result属性 Result res = new Result(result.attributeValue("name"), result.getText(),Boolean.valueOf(result.attributeValue("redirect"))); mapping.getResults().put(res.getName(),res); } //保存配置信息 mappings.put(mapping.getName(),mapping); } } catch (DocumentException e) { e.printStackTrace(); } } }
写好核心控制器之后,在web.xml中部署该ActionServlet:
<?xml version="1.0" encoding="UTF-8"?> <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee " xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance " xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd "> <servlet> <servlet-name>ActionServlet</servlet-name> <servlet-class>org.wp.servlet.ActionServlet</servlet-class> <init-param> <param-name>configName</param-name> <param-value>mystruts.xml</param-value> </init-param> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>ActionServlet</servlet-name> <url-pattern>*.action</url-pattern> </servlet-mapping> </web-app>
然后定义Action接口:
package org.wp.action; /** * 定义Action接口 */ public interface Action { //定义结果试图常量 public static final String SUCCESS = "success" ; public static final String INPUT = "input" ; public static final String ERROR = "error" ; public static final String LOGIN = "login" ; /** * 定义execute方法 * @return String * @throws Exception */ public String execute() throws Exception; }
提供一个实现了Action接口的实现类ActionSupport:
package org.wp.action; /** * 定义ActionSupport并实现Action接口 * */ public class ActionSupport implements Action { /** * 实现execute方法,并返回SUCCESS试图 */ public String execute() throws Exception { return SUCCESS; } }
然后编写自己的Action类LoginAction来处理登陆:
package entity; package org.wp.action; public class LoginAction extends ActionSupport { private String username ;//接收与表单域名称相同的参数值 private String password ; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String execute() throws Exception { if(username.equals("admin")&&password.equals("admin")) return SUCCESS; else return INPUT ; } }
然后在mystruts.xml对请求和视图进行配置:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mystruts[ <!ELEMENT mystruts (actions*)> <!ELEMENT actions (action*)> <!ELEMENT action (result*)> <!ATTLIST action name CDATA #REQUIRED class CDATA #REQUIRED> <!ATTLIST result name CDATA #REQUIRED redirect (true|false) "true"> ]> <mystruts> <actions> <action name="login" class="org.wp.action.LoginAction"> <result name="success">/register.jsp</result> <result name="input">/index.jsp</result> </action> </actions> </mystruts>
这样一个自定义的struts框架就完成了,看看也不是很复杂吧!然后我们通过访问http://localhost:8080/MyStruts/login.jsp 提交后ActionServlet就会进行拦截,然后就会去mystruts.xml中去找到与提交的action名相同的对应的Action处理类,通过执行execute方法来跳转到相应的视图。