MVC 模式 (二)
MVC(二)主要是对MVC(一)的优化,这里看不懂的朋友可以去我的一里面看看,理解MVC的基本架构。在这里需要提到的是,我们需要使用2个架包,一个dom4j,对xml进行解析,一个是反射需要用到的。
文章目录
1.将Action的信息配置到xml(反射实例化)
首先我们来看一下我们的文件结构
我们的com.zking.web下有五个文件,其中一个是我们的其他四个的优化,CalAction.java
下面的是我们的“主控制器”,里面被注释的代码就是我们所优化的代码。
package com.zking.framework;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.beanutils.BeanUtils;
/**
* 主控制器
*
* @author huguiyun
*
*/
public class DispatcherSelelet extends HttpServlet {
private static final long serialVersionUID = 4610137993952463128L;
// private Map<String, Action> actionMap = new HashMap<>();
private ConfilgModel configModel;
@Override
public void init() throws ServletException {
// actionMap.put("/addcla", new AddCalAction());// 加
// actionMap.put("/delCla", new DelCalAction());// 减
// actionMap.put("/chenCla", new chenCalAction());//乘
// actionMap.put("/chuCla", new chuCalAction());// 除
try {
// 将原有的读取框架的默认配置文件转变成读取可配置路径的配置文件
String xmlPath = this.getInitParameter("xmlPath");//找到我们的文件,前提是需要先配置我们的web.xml
if (xmlPath == null && "".equals(xmlPath)) {
configModel = configModelFactory.build();//这个是调用我们configModelFactory里的方法
} else {
configModel = configModelFactory.build(xmlPath);
}
} catch (Exception e) {
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();
url = url.substring(url.lastIndexOf("/"), url.lastIndexOf("."));
// System.out.println(url);
// Action action = actionMap.get(url);
// System.out.println(action);
// action.execute(req, resp);
ActonModel actionModel = configModel.pop(url);//
String type = actionModel.getType();// 获取全路径名
if (actionModel == null) {
throw new RuntimeException("你没有配置对应的子控制器Action!!!");
}
try {
// 将原来子控制器的来源是map集合,这样的话子控制会被写死在map容器中,代码不够灵活
// 现在将自控制器以配置的方式放config.xml中,未来可以通过改变config。xml中的内容
// 随意给中央控制器添加子控制器
Action action = (Action) Class.forName(type).newInstance();// 反射实例化,继承Action
// 调用模型驱动接口,获取所需要操作的实体类,然后将jsp传递过来的参数,分装到实体类中
if (action instanceof ModelDriven) {// 如果说它实现了这个接口,向上提升
ModelDriven modelDroven = (ModelDriven) action;
Object model = modelDroven.getModel();// 调用方法
// Map<String, String[]> map = req.getParameterMap();
// for(Map.Entry<String, String[]> entry : map.entrySet()) {
可以获取到类对应属性banem,获到类所对应的属性值
// }
// 所有参数都分装到model
BeanUtils.populate(model, req.getParameterMap());
}
// 每个子控制器,都需要对结果进行对应的处理,也就是说要么转发,要么重定向,代码重复量较大
// 针对于这一现象,将其交给配置文件来处理
// 调用了增强版的子控制器来处理业务逻辑
String code = action.execute(req, resp);// 路径跳转的页面
ForwardModel forwardModel = actionModel.pop(code);
if (forwardModel == null) {
throw new RuntimeException("你没有配置对应的子控制器Action的处理方式!!!");
}
String jspPath = forwardModel.getPath();// 通过建模拿到path里的路劲
if (forwardModel.isRedirect()) {// 通过建模取到redirect里的值,如果是true就重定向
resp.sendRedirect(jspPath);
} else {// 是false的话就转发
req.getRequestDispatcher(jspPath).forward(req, resp);
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
思路:
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和代码判断,来使得框架的配置文件可变
下面是我们配置的web.xml文件
<servlet>
<servlet-name>dispatcherSelelet</servlet-name>
<servlet-class>com.zking.framework.DispatcherSelelet</servlet-class>
<init-param>//这一部分就是我们自己写的配置文件
<param-name>xmlPath</param-name>
<param-value>/mvc.xml</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherSelelet</servlet-name>
<url-pattern>*.action</url-pattern>
</servlet-mapping>
下面就是我们自己写的配置文件mvc.xml
redirect是判断我们是文件跳转的方式,false是转发,true是重定向。在这里我们用到的跳转方式都是转发
path.jsp 我们要跳转到的jsp界面
<?xml version="1.0" encoding="UTF-8"?>
<config>
<action path="/cal" type="com.zking.web.CalAction">
<forward name="calRes" path="/calRes.jsp" redirect="false" />
</action>
</config>
这是我们ConfilgModel
package com.zking.framework;
import java.util.HashMap;
import java.util.Map;
public class ConfilgModel {
private Map<String, ActonModel> acMap = new HashMap<>();
public void push(ActonModel actionModel) {
acMap.put(actionModel.getPath(), actionModel);
}
public ActonModel pop(String path) {
return acMap.get(path);
}
}
2.将一组相关的操作放到一个Action中(反射调用方法)
子控制器Action
package com.zking.framework;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface Action {
String execute(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException;
}
加强版的Action,一个页面可以一对多,而不是一对一
package com.zking.framework;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 之前的Action只能处理一个实体类的一个业务
*
* 现在这个是增强版的子控制器 凡是这个实体类的操作,对应方法都可以写在当前增强版的子控制器来完成
*
*
* @author huguiyun
*
*/
public class ActionSupport implements Action {
@Override
public final 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对象
method.setAccessible(true);// 打开访问权限
// 具体调用了你自己所写的子控制器中的方法来处理游览器请求
code = (String) method.invoke(this, req, resp);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return code;
}
}
3.利用ModelDriver接口对Java对象进行赋值(反射读写方法)
ModelDriver 模型驱动接口,
package com.zking.framework;
/**
* 模型驱动接口 是用来处理jsp界面传递过来的参数, 将所有的参数分装实体类T中
*
* @author huguiyun
*
* @param <T>
*/
public interface ModelDriven<T> {
T getModel();
}
中央控制器调用模型接口的地方
// 调用模型驱动接口,获取所需要操作的实体类,然后将jsp传递过来的参数,分装到实体类中
if (action instanceof ModelDriven) {// 如果说它实现了这个接口,向上提升
ModelDriven modelDroven = (ModelDriven) action;
Object model = modelDroven.getModel();// 调用方法
// Map<String, String[]> map = req.getParameterMap();
// for(Map.Entry<String, String[]> entry : map.entrySet()) {
可以获取到类对应属性banem,获到类所对应的属性值
// }
// 所有参数都分装到model
BeanUtils.populate(model, req.getParameterMap());
}
4.使得框架的配置文件可变
try {
// 将原有的读取框架的默认配置文件转变成读取可配置路径的配置文件
String xmlPath = this.getInitParameter("xmlPath");
if (xmlPath == null && "".equals(xmlPath)) {
configModel = configModelFactory.build();
} else {
configModel = configModelFactory.build(xmlPath);
}
} catch (Exception e) {
e.printStackTrace();
}
在上面调用了下面的这个方法
package com.zking.framework;
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 ConfilgModel build() throws DocumentException {
return build("config.xml");
}
public static ConfilgModel build(String xmlPath) throws DocumentException {
ConfilgModel ConfigModel = new ConfilgModel();
InputStream in = configModelFactory.class.getResourceAsStream(xmlPath);
SAXReader saxReader = new SAXReader();
Document doc = saxReader.read(in);
ActonModel actionModel = null;
ForwardModel forwardModel = null;
List<Element> actionEles = doc.selectNodes("/config/action");
for (Element actionEle : actionEles) {
actionModel = new ActonModel();
actionModel.setPath(actionEle.attributeValue("path"));
actionModel.setType(actionEle.attributeValue("type"));
List<Element> forwardEles = actionEle.selectNodes("forward");
for (Element forwardEle : forwardEles) {
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 {
ConfilgModel congigModel = configModelFactory.build();
ActonModel actionModel = congigModel.pop("/loginAction");
System.out.println(actionModel.getType());
ForwardModel popo = actionModel.pop("success");
System.out.println(popo.getName() + " " + popo.isRedirect());
}
}
总结:自定义的MVC模式
定义ActionSupport 子控制器,是用来放置Action的节点的
定义DispatcherSelelet 中央控制器,管理ActionSupport
定义ActonModel利用反射机制,找到具体类的实列
写的有点乱,不喜勿喷