MVC自定义框架
昨天我们写了一个计算器:今天仍以计算器为例子,我们来简化一下昨天的代码量
首先我们今天的大纲是:
1 将Action的信息配置到xml(反射实例化)
2 通过结果码控制页面的跳转
3 将一组相关的操作放到一个Action中(反射调用方法)
4 利用ModelDriver接口对Java对象进行赋值(反射读写方法)
5 使得框架的配置文件可变
将Action的信息配置到xml(反射实例化)
在之前我们写过xml的配置解析 现在我们就可以运用之前的解析来写啦
ConfigModel
public class ConfigModel {
private Map<String, ActionModel> acmap=new HashMap<>();
public void push(ActionModel actionmodel) {
acmap.put(actionmodel.getPath(), actionmodel);
}
public ActionModel pop(String path) {
return acmap.get(path);
}
}
ActionModel
public class ActionModel {
private String path;
private String type;
private Map<String, ForwardModel> fMap=new HashMap<>();
public String getPath() {
return path;
}
public void setPath(String path) {
this.path = path;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public void push(ForwardModel forwardmodel) {
fMap.put(forwardmodel.getName(),forwardmodel);
}
public ForwardModel pop(String name) {
return fMap.get(name);
}
}
ForwardModel
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;
}
}
ConfigModelFatory 文件加工厂
public class public class ConfigModelFatory {{
public static ConfigModel build() throws Exception {
return build("config.xml");
}
public static ConfigModel build(String string) throws Exception {
ConfigModel configmodel=new ConfigModel();
InputStream in = ConfigModelFatory.class.getResourceAsStream("config.xml");
SAXReader reader=new SAXReader();
Document doc = reader.read(in);
ActionModel actionmodel=null;
ForwardModel forwardmodel=null;
List<Element> actionEles = doc.selectNodes("/config/action");
for (Element actionEle : actionEles) {
actionmodel=new ActionModel();
//接下来需要往actionModel中填充内容
actionmodel.setPath(actionEle.attributeValue("path"));
actionmodel.setType(actionEle.attributeValue("type"));
//拿到foward
List<Element> forword = actionEle.selectNodes("forward");
for (Element forwardEle : forword) {
forwardmodel=new ForwardModel();
//往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;
}
}
//注意导包,不然会报错
//注: 父类不允许抛得异常子类也不允许抛
思路:
1 将Action的信息配置到xml(反射实例化):
1.1:将原来控制器的来源是Map集合这样的话
子控制器会被写死在map容器中,代码不够灵活,
1.2:现在将子控制器以配置的方式存放在config.xml中,
未来可以通过改变config.xml中的内容
2 通过结果码控制页面的跳转
2.1.每个子控制器都需要对结果经行对应的处理,也就是说 要么转发要么重定向代码重复量较大
针对于这一现象,将其交给配置文件来处理
3 将一组相关的操作放到一个Action中(反射调用方法)
3.1增强我们的子控制器,也就是把我们所有的方法全部放进一个类中,这个类来实现我们的子控制器接口,然后通过我们传过来的方法和反射动态调用方法。
4 利用ModelDriver接口对Java对象进行赋值(反射读写方法)
4.1调用模型驱动接口 获取所要操作的实体类,然后将jsp传递过来的参数,封装到实体类中
5 使得框架的配置文件可变
5.1 通过配置web.xml和代码判断,来使得框架的配置文件可变
/**
* 中央控制器
* @author XYX
* 父类不允许抛得异常子类也不允许抛
*
*/
public class DispathcherServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
//让文件可配置
private ConfigModel configModel=null;
//一一对应的关系
// private Map<String, Action> actionMap=new HashMap<>();
//初始化的方法
public void init() {
// actionMap.put("/addCal",new AddCalAction());
// actionMap.put("/delCal",new DelCalAction2());
// actionMap.put("/cheCal",new CheCalAction());
// actionMap.put("/chuCal",new ChuCalAction());
try {
//将原有的读取框架的默认配置文件转换为读取可配置路径的配置文件
String xmlPath = this.getInitParameter("xmlPath");
if(xmlPath==null|| "".equals(xmlPath))
configModel=ConfigModelFactory.build();
else
configModel=ConfigModelFactory.build(xmlPath);
configModel = ConfigModelFactory.build();
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
init();
String url = req.getRequestURI();//获取xxx.action
url=url.substring(url.lastIndexOf("/"), url.lastIndexOf("."));
ActionModel actionModel = configModel.pop(url);//获取路径
if(actionModel==null) {//如果说你没有config.xml
//抛出异常
throw new RuntimeException("你没有配置对应的子控制器Action!!!");
}
//<action path="/loginAction" type="test.action.LoginAction">
//通过/loginAction 拿到 test.action.LoginAction
//String type=actionModel.getType();
//一切反射由获取类对象开始
try {
//将原来控制器的来源是Map集合这样的话子控制器会被写死在map容器中,代码不够灵活,
//现在将子控制器以配置的方式存放在config.xml中,未来可以通过改变config.xml中的内容
// 随意给中央控制器添加子控制器
//获取类对象 并实列化,默认调用无参构造器
Action action = (Action)Class.forName(actionModel.getType()).newInstance();
//code代表的是路径,是什么路径就返回什么路径
//调用模型驱动接口 获取所要操作的实体类,然后将jsp传递过来的参数,封装到实体类中
if(action instanceof ModelDriven) {//实现
ModelDriven modelDriven=(ModelDriven)action;//向上提升
Object model = modelDriven.getModel();
// Map<String, String[]> map = req.getParameterMap();
// Set<Entry<String, String[]>> entrySet = map.entrySet();
// for (Map.Entry<String, String[]> entry : map.entrySet()) {
// //可以获取到对应的属性bname,获取到对应的属性值
//
// }
BeanUtils.populate(model, req.getParameterMap());
}
//每个子控制器都需要对结果经行对应的处理,也就是说 要么转发要么重定向代码重复量较大
//针对于这一现象,将其交给配置文件来处理
//调用了争抢版的子控制器来处理业务逻辑
String code = action.execute(req, resp);
//<forward name="b" path="/welcome.jsp" redirect="true" />
//通过nane 找到它的路径path
ForwardModel forwardModel = actionModel.pop(code);
if(forwardModel==null) {//如果说你没有config.xml
//抛出异常看他是否有forward
throw new RuntimeException("你没有配置对应的子控制器Action的处理方式forward!!!");
}
//获取要跳转的页面 获取路径
String jspPath=forwardModel.getPath();
if(forwardModel.isRedirect()) {
resp.sendRedirect(req.getContextPath()+jspPath);//重定向
}else {
//转发
req.getRequestDispatcher(jspPath).forward(req, resp);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//相当于获取到了test.action.LoginAction
}
}
子控制器Action
/**
* 子控制器
* 专门用来处理业务逻辑层的
* @author XYX
*
*/
public interface Action {
//用来处理请求
String execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;
}
现在这个是增强版的子控制器:一个页面可以一对多,而不是一对一
/**
* 之前的Action只能处理一个实体类的一个业务
*
* 现在这个是增强版的子控制器
* 凡是这个实体类的操作,对应方法都可以写在当前增强版的子控制器来完成;
* @author XYX
*
*/
public class ActionSupport implements Action{
@Override
public String execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String methodName = req.getParameter("methodName");
//声明一个返回值
String code=null;
//获取方法的类对象
try {
Method method = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
method.setAccessible(true);//打开访问权限
try {
//具体调用浏览器方法来处理浏览器请求
code = (String)method.invoke(this, req,resp);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
return code;
}
}
模型驱动接口
package com.zking.framework;
/**
* 模型驱动接口
* 是用来处理jsp页面传递过来的参数,
* 将所有的参数自动封装到实体类<T>中
* @author XYX
*
* @param <T>
*/
public interface ModelDriven<T> {
//每个实体类都会封装 所有用接口
T getModel();
}
区别注意的xml与我们简化代码的区别
<?xml version="1.0" encoding="UTF-8"?>
<config>
<!-- <action path="/addCal" type="com.zking.web.AddCalAction">
<forward name="calRes" path="/calRes.jsp" redirect="false" />
</action>
<action path="/delCal" type="com.zking.web.DelCalAction">
<forward name="calRes" path="/calRes.jsp" redirect="false" />
</action>
<action path="/cheCal" type="com.zking.web.CheCalAction">
<forward name="calRes" path="/calRes.jsp" redirect="false" />
</action>
<action path="/chuCal" type="com.zking.web.ChuCalAction">
<forward name="calRes" path="/calRes.jsp" redirect="false" />
</action> -->
<action path="/cal" type="com.zking.web.CalAction">
<forward name="calRes" path="/calRes.jsp" redirect="false" />
</action>
</config>
此时我们再注意一下web.xml是不是配置比以前的少了呢,以前是一个配置一个,现在是多个配置一个
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
<display-name>T224_mvc</display-name>
<servlet>
<servlet-name>dispathcherServlet</servlet-name>
<servlet-class>com.zking.framework.DispathcherServlet</servlet-class>
<init-param>
<param-name>xmlPath</param-name>
<param-value>/mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispathcherServlet</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
</web-app>
//接下啦再来看我们输出的页面:
<%@ 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 here</title>
<script type="text/javascript">
function doSub(val) {
if(val==1){
//calForm.action="${pageContext.request.contextPath }/addCal.action";
calForm.methodName.value="add";
}else if(val==2){
//calForm.action="${pageContext.request.contextPath }/delCal.action";
calForm.methodName.value="del";
}else if(val==3){
//calForm.action="${pageContext.request.contextPath }/cheCal.action";
calForm.methodName.value="che";
}
else if(val==4){
//calForm.action="${pageContext.request.contextPath }/chuCal.action";
calForm.methodName.value="chu";
}
calFrom.submit();
}
</script>
</head>
<body>
<form id="calForm" name="calForm" action="${pageContext.request.contextPath }/cal.action">
num1:<input type="text" name="num1"><br>
num2:<input type="text" name="num2"><br><br>
<input type="hidden" name="methodName">
<button onclick="doSub(1)">+</button>
<button onclick="doSub(2)">-</button>
<button onclick="doSub(3)">*</button>
<button onclick="doSub(4)">/</button>
</form>
</body>
</html>
跳转到calRes.jsp页面上:
<%@ 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 here</title>
</head>
<body>
结果:${res }
</body>
</html>
好了,这就是今天的代码了