自己写框架-MVC(四)
技术要点:
xml解析
监听器
java多态特性
反射
改造计划:
模仿struts实现通过xml文件来配置请求对应的action处理器、请求表单、跳转路径,整个过程由框架自动完成(创建分发action处理器、封装formbean、请求跳转)。
1、创建mvc配置文件mvc-config
resource/mvc-config.xml代码清单:
1 2 3 4 5 6 7 8 9 10 11 12 | <?xml version="1.0" encoding="UTF-8"?> <mvc-config> <action name="login" class="cn.jslfl.mvc.demo.action.LoginAction" form="cn.jslfl.mvc.demo.form.LoginForm"> <result name="success">/web/userInfo.jsp</result> <result name="error">/web/login.jsp</result> </action> <action name="reg" class="cn.jslfl.mvc.demo.action.RegAction" form="cn.jslfl.mvc.demo.form.RegForm"> <result name="success">/web/login.jsp</result> <result name="error">/web/reg.jsp</result> </action> </mvc-config> |
2、mvc-config.xml解析处理
2.1、创建MvcConfig.java,定义配置文件结构名称常量
MvcConfig.java代码清单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package cn.jslfl.mvc.core.config; /** * mvc配置文件结构名称定义 * @author jslfl * */ public class MvcConfig { public static final String ACTION_ATTR = "attr"; public static final String ACTION_RESULT = "result"; public static final String ACTION_NAME = "name"; public static final String ACTION_CLASS = "class"; public static final String ACTION_FORM = "form"; public static final String RESULT_NAME = "name"; public static final String MVC_CONFIG = "mvc_config"; } |
2.2、创建ParseMvcConfig.java来做mvc-config.xml解析处理
ParseMvcConfig代码清单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 | package cn.jslfl.mvc.core.config; import java.io.File; import java.util.HashMap; import java.util.List; import java.util.Map; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; /** * mvc配置文件解析操作 * @author jslfl * */ public class ParseMvcConfig { /** * 解析完成后返回数据结构示例 * { * login: * { * attr:{name:'login', class:'cn.action.Login', form:'cn.form.LoginForm'}, * result:{success:'login.jsp', error:'error.jsp'} * }, * reg: * { * attr:{name:'reg', class:'cn.action.Reg', form:'cn.form.RegForm'}, * result:{success:'login.jsp', error:'error.jsp'} * } * } */ @SuppressWarnings("unchecked") public static Map<String, Map<String,Map<String,String>>> parse(String xmlPath){ Map<String, Map<String,Map<String,String>>> actionMap = new HashMap<String, Map<String,Map<String,String>>>(); SAXReader reader = new SAXReader(); try { Document doc = reader.read(new File(xmlPath)); Element root = doc.getRootElement(); List<Element> actionElementList = root.elements(); for(Element actionElement : actionElementList){ Map<String, Map<String,String>> action = new HashMap<String, Map<String,String>>(); //action节点属性 String actionName = actionElement.attribute(MvcConfig.ACTION_NAME).getStringValue(); Map<String,String> attr = new HashMap<String,String>(); attr.put(MvcConfig.ACTION_NAME, actionName); attr.put(MvcConfig.ACTION_CLASS, actionElement.attribute(MvcConfig.ACTION_CLASS).getStringValue()); attr.put(MvcConfig.ACTION_FORM, actionElement.attribute(MvcConfig.ACTION_FORM).getStringValue()); //action节点下result子节点 List<Element> resultElementList = actionElement.elements(); Map<String,String> result = new HashMap<String,String>(); for(Element resultElement : resultElementList){ result.put(resultElement.attributeValue(MvcConfig.RESULT_NAME), resultElement.getText()); } action.put(MvcConfig.ACTION_ATTR, attr); action.put(MvcConfig.ACTION_RESULT, result); actionMap.put(actionName, action); } } catch (DocumentException e) { e.printStackTrace(); } return actionMap; } } |
本例中使用dom4j来解析xml,所以需要引入dom4j的jar包,并且解析结果使用Map来存储,最好方式是定义相应pojo类来存储。
3、创建监听器ActionServletListener.java,ActionServlet初始化时调用解析mvc-config.xml,结果存入servlet上下文
ActionServletListener.java代码清单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | package cn.jslfl.mvc.core.listener; import java.util.Map; import javax.servlet.ServletContextEvent; import javax.servlet.ServletContextListener; import cn.jslfl.mvc.core.config.MvcConfig; import cn.jslfl.mvc.core.config.ParseMvcConfig; /** * ActionServlet初始化时解析mvc-config.xml * @author jslfl */ public class ActionServletListener implements ServletContextListener { @Override public void contextDestroyed(ServletContextEvent arg0) { System.out.println("mvc-framework: 系统退出!"); } @Override public void contextInitialized(ServletContextEvent arg0) { String xmlPath = arg0.getServletContext().getInitParameter("mvc-config"); System.out.println("mvc-framework: 取得参数mvc配置文件路径: " + xmlPath);//print================ String mvcXmlPath = this.getClass().getClassLoader().getResource(xmlPath).getPath();//类路径下的配置文件 System.out.println("mvc-framework: 取得参数mvc配置文件路径: " + mvcXmlPath);//print================ //解析mvc配置文件 Map<String, Map<String,Map<String,String>>> actionMap = ParseMvcConfig.parse(mvcXmlPath); System.out.println("mvc-framework: mvc配置加载成功!");//print================ System.out.println("mvc-framework: mvc配置信息:");//print================ System.out.println(actionMap);//print================ arg0.getServletContext().setAttribute(MvcConfig.MVC_CONFIG, actionMap); } } |
4、改造formbean
4.1、定义formbean的基类
ActionFormBean.java代码清单:
1 2 3 4 5 6 7 8 | package cn.jslfl.mvc.core.form; /** * mvc 中from表单的父类 * @author jslfl */ public class ActionFormBean { } |
4.2、FormBeanUtil.java改名为ActionFormBeanUtil.java,并返回ActionFormBean对象
FormBeanUtil.java代码清单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | package cn.jslfl.mvc.core.form; import java.lang.reflect.Field; import javax.servlet.http.HttpServletRequest; /** * 自动解析封装FormBean对象 * @author jslfl * */ public class ActionFormBeanUtil { @SuppressWarnings("rawtypes") public static ActionFormBean packFormBean(HttpServletRequest req, String formBeanName){ ActionFormBean form = null; try { Class c = Class.forName(formBeanName); form = (ActionFormBean)c.newInstance(); Field[] fieldArr = c.getDeclaredFields(); for(Field f : fieldArr){ f.setAccessible(true); f.set(form, req.getParameter(f.getName())); f.setAccessible(false); } } catch (Exception e) { e.printStackTrace(); } return form; } } |
4.3、重构LoginForm、RegForm,让它们继承ActionFormBean类
LoginForm.java代码清单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | package cn.jslfl.mvc.demo.form; import cn.jslfl.mvc.core.form.ActionFormBean; public class LoginForm extends ActionFormBean{ private String userName; private String passWord; public LoginForm(){ } 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; } } |
RegForm.java代码清单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | package cn.jslfl.mvc.demo.form; import cn.jslfl.mvc.core.form.ActionFormBean; public class RegForm extends ActionFormBean { private String userName; private String passWord; private String passWord2; public RegForm(){ } public String getPassWord2() { return passWord2; } public void setPassWord2(String passWord2) { this.passWord2 = passWord2; } 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; } } |
5、改造action
5.1、创建action接口MvcAction.java
MvcAction.java代码清单:
1 2 3 4 5 6 7 8 9 10 11 12 13 | package cn.jslfl.mvc.core.action; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.jslfl.mvc.core.form.ActionFormBean; /** * Action接口 * @author jslfl */ public interface MvcAction { public String doService(ActionFormBean formBean, HttpServletRequest req, HttpServletResponse resp); } |
5.2、重构LoginAction、RegAction,实现MvcAction接口,doService调用参数改为ActionFormBean
LoginAction代码清单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | package cn.jslfl.mvc.demo.action; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.jslfl.mvc.core.action.MvcAction; import cn.jslfl.mvc.core.form.ActionFormBean; import cn.jslfl.mvc.demo.form.LoginForm; public class LoginAction implements MvcAction { public LoginAction(){ } public String doService(ActionFormBean actionFormBean, HttpServletRequest req, HttpServletResponse resp){ LoginForm formBean = (LoginForm)actionFormBean; //业务处理开始... boolean flag = false; if("aa".equals(formBean.getUserName()) && "bb".equals(formBean.getPassWord())){ flag = true; } //业务处理结束... String info = "<h1>登录" + (flag ? "成功" : "失败") + "!userName: " + formBean.getUserName()+ ", " + "password: " + formBean.getPassWord() + "</h1>"; req.setAttribute("info", info); if(flag) return "success"; return "error"; } } |
RegAction代码清单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package cn.jslfl.mvc.demo.action; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.jslfl.mvc.core.action.MvcAction; import cn.jslfl.mvc.core.form.ActionFormBean; import cn.jslfl.mvc.demo.form.RegForm; public class RegAction implements MvcAction { public RegAction(){ } public String doService(ActionFormBean actionFormBean, HttpServletRequest req, HttpServletResponse resp){ RegForm regBean = (RegForm)actionFormBean; //业务处理开始... String msg = ""; if(regBean.getUserName() == null || "".equals(regBean.getUserName().trim()) ||regBean.getPassWord() == null || "".equals(regBean.getPassWord().trim()) || regBean.getPassWord2() == null || "".equals(regBean.getPassWord2().trim())){ msg = "数据不完整"; }else if(!regBean.getPassWord().equals(regBean.getPassWord2())){ msg = "两次密码不匹配"; }else{ msg = "成功"; } //业务处理结束... String info = "<h1>注册" + msg + "!userName: " + regBean.getUserName() + ", " + "password: " + regBean.getPassWord() + ", " + "password2: " + regBean.getPassWord2() +"</h1>"; req.setAttribute("info", info); if("成功".equals(msg)) return "success"; return "error"; } } |
6、改造ActionServlet
从ServletContext中取出mvc-config.xml的配置信息,通过actionName取得配置中请求映射action的信息,再创建FormBean、创建Action,调用action.doService,取得返回路径进行跳转
ActionServlet.java代码清单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 | package cn.jslfl.mvc.core.servlet; import java.io.IOException; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import cn.jslfl.mvc.core.action.MvcAction; import cn.jslfl.mvc.core.config.MvcConfig; import cn.jslfl.mvc.core.form.ActionFormBean; import cn.jslfl.mvc.core.form.ActionFormBeanUtil; public class ActionServlet extends HttpServlet{ private static final long serialVersionUID = 1L; @SuppressWarnings({ "unchecked", "rawtypes" }) public void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { resp.setCharacterEncoding("GBK"); //取得请求名称,对应action名称 String reqPath = req.getServletPath(); String actionName = reqPath.substring(1, reqPath.lastIndexOf(".do"));//截取 "/login.do" -> "login" System.out.println("mvc-framework: 请求路径: " + reqPath + " actionName: " + actionName );//print================ //得到action配置信息 Map<String, Map<String,Map<String,String>>> actionMap = (Map<String, Map<String,Map<String,String>>>)req.getServletContext().getAttribute(MvcConfig.MVC_CONFIG); Map<String,Map<String,String>> actionCfg = actionMap.get(actionName); System.out.println("mvc-framework: action信息: " + actionCfg);//print================ //自动封装FormBean ActionFormBean formBean = ActionFormBeanUtil.packFormBean(req, actionCfg.get(MvcConfig.ACTION_ATTR).get(MvcConfig.ACTION_FORM)); //调用业务action String result = ""; try { Class actionClass = Class.forName(actionCfg.get(MvcConfig.ACTION_ATTR).get(MvcConfig.ACTION_CLASS)); MvcAction action = (MvcAction)actionClass.newInstance(); result = action.doService(formBean, req, resp); } catch (Exception e) { e.printStackTrace(); } req.getRequestDispatcher(actionCfg.get(MvcConfig.ACTION_RESULT).get(result)).forward(req, resp); } public void doGet(HttpServletRequest req, HttpServletResponse resp){ try { doPost(req, resp); } catch (Exception e) { e.printStackTrace(); } } } |
7、修改web.xml配置
web.xml代码清单:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0"> <display-name>mvc_framework</display-name> <!-- mvc framework配置 开始--> <context-param> <param-name>mvc-config</param-name> <param-value>/mvc-config.xml</param-value> </context-param> <listener > <listener-class>cn.jslfl.mvc.core.listener.ActionServletListener</listener-class> </listener> <servlet> <servlet-name>mvc</servlet-name> <servlet-class>cn.jslfl.mvc.core.servlet.ActionServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>mvc</servlet-name> <url-pattern>*.do</url-pattern> </servlet-mapping> <!-- mvc framework配置 结束--> </web-app> |
问题:action 中ActionFormBean应该直接用子类
本例只是对MVC基础原理做演示,故不做数据库操作和深入严谨的业务处理.
LoginForm、RegForm中一般需要实现Serializable接口,并提供无参构造方法,以支持序列化和保证反射创建对象时(在重载了构造方法时)不出错。
总结:
Comments are currently closed.