虽然前面文章深入解析Java自定义MVC框架的原理与实现讲述了Mvc框架,但是那只能算是比较低级的,还能够再优化,接下来我会再优化3次代码,让代码更为简洁,通用。
虽然简化了代码,但是代码并不 能够通用,MVC这种东西,最终是要打包成jar架包的,可是上一篇文章的代码优化并不能够打包成架包,现在开始第一次优化。
DOCTYPE config
[ELEMENT config (action*)>
<!ELEMENT action (forward*)>
<!ATTLIST action
path CDATA #REQUIRED
type CDATA #IMPLIED
>
]
>
<config>
<action path="/book" type="com.niyin.com.BookAction">
<forward name="list" path="/res.jsp" redirect="true" />
<forward name="tolist" path="/res.jsp" redirect="false" />
action>
<action path="/order" type="com.niyin.com.Orderaction">
<forward name="success" path="/index.jsp" redirect="true" />
<forward name="failed" path="/register.jsp" redirect="false" />
action>
<action path="/bookAction" type="test.BookAction">
<forward name="add" path="/bookAdd.jsp" redirect="true" />
<forward name="del" path="/reg.jsp" redirect="false" />
<forward name="list" path="/list.jsp" redirect="false" />
<forward name="upd" path="/login.jsp" redirect="false" />
action>
<action path="/loginAction" type="test.action.LoginAction">
<forward name="a" path="/index.jsp" redirect="false" />
<forward name="b" path="/welcome.jsp" redirect="true" />
action>
config>
package model;
import java.util.HashMap;
import java.util.Map;
public class ActionModel {
private String path;
private String type;
private Map<String ,ForwardModel>fmp=new HashMap<String ,ForwardModel>();
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public void push(ForwardModel forwardModel) {
fmp.put(forwardModel.getName(), forwardModel);
}
public ForwardModel pop(String name) {
return fmp.get(name);
}
}
package model;
import java.util.HashMap;
import java.util.Map;
public class ConfigModel {
private Map<String,ActionModel>aMap=new HashMap<String,ActionModel>();
public void push(ActionModel actionModel) {
aMap.put(actionModel.getPath(), actionModel);
}
public ActionModel pop(String path) {
return aMap.get(path);
}
public static void main(String[] args) throws Exception {
ConfigModel configModel =new ConfigMOdelFactory().build();
ActionModel actionModel=configModel.pop("/loginAction");
ForwardModel forwardModel =actionModel.pop("b");
System.out.println(forwardModel.getPath());
}
}
package model;
import java.io.InputStream;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
public class ConfigMOdelFactory {
public static ConfigModel build() throws Exception {
ConfigModel configModel = new ConfigModel();
InputStream in = ConfigMOdelFactory.class.getResourceAsStream("/config.xml");
SAXReader sr = new SAXReader();
Document doc = sr.read(in);
List<Element> actionsEles = doc.selectNodes("/config/action");
for (Element element : actionsEles) {
// System.out.println(element.asXML());
ActionModel actionmodel = new ActionModel();
actionmodel.setPath(element.attributeValue("path"));
actionmodel.setType(element.attributeValue("type"));
List<Element> forwardEles = element.selectNodes("forward");
for (Element forwardEle : forwardEles) {
// System.out.println(forwardEle.asXML());
ForwardModel forwardModel = new ForwardModel();
forwardModel.setName(forwardEle.attributeValue("name"));
forwardModel.setPath(forwardEle.attributeValue("path"));
forwardModel.setRedirect(!"false".equals(forwardEle.attributeValue("redirect")));
actionmodel.push(forwardModel);
}
configModel.push(actionmodel);
}
return configModel;
}
public static void main(String[] args) throws Exception {
ConfigMOdelFactory.build();
}
}
package model;
import java.util.HashMap;
import java.util.Map;
public class ForwardModel {
private String name;
private String path;
private boolean redirect;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public boolean isRedirect() {
return redirect;
}
public void setRedirect(boolean redirect) {
this.redirect = redirect;
}
}
以前把所有的子控制器放到map集合中,现在所有的子控制器再config。xml中。
package com.niyin.framework;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import com.niyin.com.BookAction;
import com.niyin.com.Orderaction;
import model.ActionModel;
import model.ConfigMOdelFactory;
import model.ConfigModel;
import model.ForwardModel;
@WebServlet("*.action")
public class DispatherServlet extends HttpServlet {
private ConfigModel configMOdel;
// public Map actionMap=new HashMap();
@Override
public void init() throws ServletException {
// actionMap.put("/book", new BookAction());
// actionMap.put("/order", new Orderaction());
// actionMap.put("/cat", new CatAction());
try {
configMOdel=ConfigMOdelFactory.build();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String uri = request.getRequestURI();
uri=uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
// Action action = actionMap.get(uri);
// 要通过uri=book/再configMOdel中找
ActionModel actionModel = configMOdel.pop(uri);
if (actionModel==null)
throw new RuntimeException("action 没有配置");
String type = actionModel.getType();
Action action;
try {
action = (Action) Class.forName(type).newInstance();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
把map集合取消,改为用模型的方式来代替map集合,如果要新增类的话只需要新增模型对象即可,不用再加入map集合中,让代码更易于维护和修改,也可以封装架包提供了可行性。
代码测试
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title heretitle>
head>
<body>
<p>版本1p>
<a href="Bookservletadd">增加a>
<a href="Bookservletupd">修改a>
<a href="Bookservletdel">减少a>
<a href="Bookservletlist">查询a>
<p>版本2p>
<a href="Bookservletadd?methodName=add">增加a>
<a href="Bookservletupd?methodName=upd">修改a>
<a href="Bookservletdel?methodName=del">减少a>
<a href="Bookservletlist?methodName=list">查询a>
<p>版本3p>
<a href="Bookservletadd?methodName=add">增加a>
<a href="Bookservletupd?methodName=upd">修改a>
<a href="Bookservletdel?methodName=del">减少a>
<a href="Bookservletlist?methodName=list">查询a>
<p>版本4p>
<a href="book.action?methodName=add">增加a>
<a href="book.action?methodName=upd">修改a>
<a href="book.action?methodName=del">减少a>
<a href="book.action?methodName=list">查询a>
版本4的弊端:中央控制器的action容器加载,不可以灵活配置
<p>版本五p>
<a href="order.action?methodName=add">增加a>
<a href="order.action?methodName=upd">修改a>
<a href="order.action?methodName=del">减少a>
<a href="order.action?methodName=list">查询a>
body>
html>
虽然上面代码优化后可以灵活配置了,但是使用重定向和转发有问题,查询必然转发,增删改使用重定向,如果频繁刷新页面,可能会出现重复提交数据。
package com.niyin.framework;
import java.io.IOException;
import java.lang.reflect.Method;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class Action {
protected String excute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String methoed = request.getParameter("methodName");
// private
String res="";
try {
Method m = this.getClass().getDeclaredMethod(methoed, HttpServletRequest.class,HttpServletResponse.class);
m.setAccessible(true);
res=(String) m.invoke(this, request,response);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return res;
}
}
把book里面的类型也改为String,并返回tolist,和list,通过返回值来判断它是需要重定向还是转发,并且还节约了代码。
package com.niyin.com;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.niyin.entity.Book;
import com.niyin.framework.Action;
import com.niyin.framework.ModelDrivern;
public class BookAction extends Action implements ModelDrivern<Book> {
private Book book=new Book();
private String list(HttpServletRequest request, HttpServletResponse response) throws Exception {
// TODO Auto-generated method stub
System.out.println("list");
// response.sendRedirect("res.jsp");
request.setAttribute("content", "你就会");
// request.getRequestDispatcher("res.jsp").forward(request, response);
return "list";
}
private String del(HttpServletRequest request, HttpServletResponse response) throws Exception {
// TODO Auto-generated method stub
System.out.println("del");
// response.sendRedirect("res.jsp");
request.setAttribute("content", "你就会");
return "tolist";
}
private String upd(HttpServletRequest request, HttpServletResponse response) throws Exception {
// TODO Auto-generated method stub
System.out.println("upd");
// response.sendRedirect("res.jsp");
request.setAttribute("content", "你就会");
return "tolist";
}
private String add(HttpServletRequest request, HttpServletResponse response) throws IOException {
// TODO Auto-generated method stub
System.out.println("add");
// response.sendRedirect("res.jsp");
request.setAttribute("content", "你就会");
return "tolist";
}
@Override
public Book getModel() {
System.out.println(book);
return book;
}
}
优化中央控制器,通过模型对象获取·forward对象来判断该重定向还是转发。
package com.niyin.framework;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import com.niyin.com.BookAction;
import com.niyin.com.Orderaction;
import model.ActionModel;
import model.ConfigMOdelFactory;
import model.ConfigModel;
import model.ForwardModel;
@WebServlet("*.action")
public class DispatherServlet extends HttpServlet {
private ConfigModel configMOdel;
// public Map actionMap=new HashMap();
@Override
public void init() throws ServletException {
// actionMap.put("/book", new BookAction());
// actionMap.put("/order", new Orderaction());
// actionMap.put("/cat", new CatAction());
try {
configMOdel=ConfigMOdelFactory.build();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String uri = request.getRequestURI();
uri=uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
// Action action = actionMap.get(uri);
// 要通过uri=book/再configMOdel中找
ActionModel actionModel = configMOdel.pop(uri);
String res= action.excute(request, response);
ForwardModel forwardModel = actionModel.pop(res);
if (forwardModel!=null) {
boolean redirect = forwardModel.isRedirect();
String path = forwardModel.getPath();
if (redirect) {
response.sendRedirect(request.getContextPath()+path);
}else {
request.getRequestDispatcher(path).forward(request, response);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
一般来说通过servlet拿到前端的值再把它传入数据库,这个过程要写很多重复代码,现在来优化一下,利用反射等等
1.先定义一个模型驱动接口
package com.niyin.framework;
/**
*
* @author 匿瘾
*
* @param
* 模型驱动接口
*/
public interface ModelDrivern<T> {
T getModel();
}
2.让bookaction实现这个接口
package com.niyin.com;
import java.io.IOException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.niyin.entity.Book;
import com.niyin.framework.Action;
import com.niyin.framework.ModelDrivern;
public class BookAction extends Action implements ModelDrivern<Book> {
private Book book=new Book();
private String list(HttpServletRequest request, HttpServletResponse response) throws Exception {
// TODO Auto-generated method stub
System.out.println("list");
// response.sendRedirect("res.jsp");
request.setAttribute("content", "你就会");
// request.getRequestDispatcher("res.jsp").forward(request, response);
return "list";
}
private String del(HttpServletRequest request, HttpServletResponse response) throws Exception {
// TODO Auto-generated method stub
System.out.println("del");
// response.sendRedirect("res.jsp");
request.setAttribute("content", "你就会");
return "tolist";
}
private String upd(HttpServletRequest request, HttpServletResponse response) throws Exception {
// TODO Auto-generated method stub
System.out.println("upd");
// response.sendRedirect("res.jsp");
request.setAttribute("content", "你就会");
return "tolist";
}
private String add(HttpServletRequest request, HttpServletResponse response) throws IOException {
// TODO Auto-generated method stub
System.out.println("add");
// response.sendRedirect("res.jsp");
request.setAttribute("content", "你就会");
return "tolist";
}
@Override
public Book getModel() {
System.out.println(book);
return book;
}
}
3.优化中央控制器
在控制器内判断有没有实现接口,如果实现了就把值传进去。
package com.niyin.framework;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
import com.niyin.com.BookAction;
import com.niyin.com.Orderaction;
import model.ActionModel;
import model.ConfigMOdelFactory;
import model.ConfigModel;
import model.ForwardModel;
@WebServlet("*.action")
public class DispatherServlet extends HttpServlet {
private ConfigModel configMOdel;
// public Map actionMap=new HashMap();
@Override
public void init() throws ServletException {
// actionMap.put("/book", new BookAction());
// actionMap.put("/order", new Orderaction());
// actionMap.put("/cat", new CatAction());
try {
configMOdel=ConfigMOdelFactory.build();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request, response);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
String uri = request.getRequestURI();
uri=uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));
// Action action = actionMap.get(uri);
// 要通过uri=book/再configMOdel中找
ActionModel actionModel = configMOdel.pop(uri);
if (actionModel==null)
throw new RuntimeException("action 没有配置");
String type = actionModel.getType();
Action action;
try {
action = (Action) Class.forName(type).newInstance();
// bookaction 有没有实现这个接口
if (action instanceof ModelDrivern) {
ModelDrivern md=(ModelDrivern) action;
Object bean = md.getModel();
Map<String, String[]> map = request.getParameterMap();
BeanUtils.populate(bean, map);
// 把值封进去
}
String res= action.excute(request, response);
ForwardModel forwardModel = actionModel.pop(res);
if (forwardModel!=null) {
boolean redirect = forwardModel.isRedirect();
String path = forwardModel.getPath();
if (redirect) {
response.sendRedirect(request.getContextPath()+path);
}else {
request.getRequestDispatcher(path).forward(request, response);
}
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
最后通过degbug查看book里面是否有值判断是否成功的,传入了值。
结果:
可以看到book里面是有值的,所以最后一个优化也成功了。
通过建模xml和反射的方式优化代码,让代码以Mvc框架的形式出现,
MVC框架构思巧妙。让代码更为简洁。