自定义MVC(二)-进一步增强
我的上一篇博客是MVC的简单减少以及有简单的案例,但是代码还是有点繁琐,比如子控制器要写四个完成,当然我们也可以用一个类来完成方法,进一步减少代码繁琐性,让思路更加清晰,接下来通过我的上一篇博客的案例进行改造让多个视图共享一个模型,大大提高代码的可重用性。
首先 我们先将我们所需要用到的工具类和jar包导入到项目中 如果需要请参考我的xml博客
我们将之前绝对路径以及后面的方法名替换成 给methodName赋值加减乘除的请求进入ActionSupport
将一组相关的操作放到一个Action中(反射调用方法) 通俗讲 之前的action只能处理一个实体类的一个业务,但凡是这个实体类的操作,对应方法都可以写在增强版的子控制器中完成
在action中 void execute 改成 String execute
每个子控制器,都需要对结果进行对应的处理,要么转发,要么重定向,代码重复量较大,针对于这一现象,将其交给配置文件来处理
/**
* 增强版的子控制器
* @author Administrator
*/
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 method = this.getClass().getDeclaredMethod(methodName,
HttpServletRequest.class,
HttpServletResponse.class);//动态获取方法
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;
}
}
CalAction是加强子控制器的子类,他不仅要继承ActionSupport 还要实现ModelDriven
public class CalAction extends ActionSupport implements ModelDriven<Cal> {
private Cal cal = new Cal();
public String add(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("res", Integer.valueOf(cal.getNum1())+Integer.valueOf(cal.getNum2()));
return "calRes";
}
public String del(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("res", Integer.valueOf(cal.getNum1())-Integer.valueOf(cal.getNum2()));
return "calRes";
}
public String ride(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("res", Integer.valueOf(cal.getNum1())*Integer.valueOf(cal.getNum2()));
return "calRes";
}
public String remove(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setAttribute("res", Integer.valueOf(cal.getNum1())/Integer.valueOf(cal.getNum2()));
return "calRes";
}
@Override
public Cal getModel() {
return cal;
}
利用ModelDriver接口对Java对象进行赋值(反射读写方法)
用来处理jsp页面传递过来的参数,将所有的参数自动封装到实体类T
/**
* 模型驱动接口
* @author Administrator
*
* @param <T>
*/
public interface ModelDriven<T> {
T getModel();
}
然后我在中央控制器中将原来子控制器的来源是map集合改成 private ConfigModel configModel = null; 如果用map集合的话子控制器会写死在map容器中,代码不够灵活,然后我现在将子控制器以配置的方式存放在config.xml中,未来可以通过改变config.xml中的内容这样就可以随意给中央控制器添加子控制器 并且将原有的读取框架的默认设置文件转变成读取可配置路径的配置文件
configModel = ConfigModelFactory.build();//默认config.xml路径
/**
* 主控制器
* @author Administrator
*
*/
public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
//一个servlet请求对应一个子控制器
// private Map<String, Action> actionMap = new HashMap<String, Action>();
private ConfigModel configModel = null;
public void init() {
try {
// 将原有的读取框架的默认设置文件转变成读取可配置路径的配置文件
String xmlPath = this.getInitParameter("xmlPath");
if (xmlPath == null || "".equals(xmlPath)) {//如果调用是配置文件是空的,就用默认的
configModel = ConfigModelFactory.build();
}else {//有配置文件,就用他写的
configModel = ConfigModelFactory.build(xmlPath);
}
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// TODO Auto-generated method stub
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
init();//提交子控制方法
String url = req.getRequestURI();//c_mvc/xxx.action
url = url.substring(url.lastIndexOf("/"), url.lastIndexOf("."));
ActionModel actionModel = configModel.pop(url);//获取路径
if (actionModel == null) {
throw new RuntimeException("你没有配置对应的子控制器Action!!");
}
try {
Action action =(Action) Class.forName(actionModel.getType()).newInstance();//获取类对象
// 调用模型驱动接口,获取所要操作的实体类,然后将jsp传递过来的参数,封装到实体类中
if (action instanceof ModelDriven) {
ModelDriven modelDriven = (ModelDriven)action;
Object model = modelDriven.getModel();
// 将所有的参数自动封装到实体类T
BeanUtils.populate(model, req.getParameterMap());
}
// 每个子控制器,都需要对结果进行对应的处理,也就是说,要么转发,要么重定向,代码重复量较大
// 针对于这一对象,将其交个配置文件处理
// 调用了增强版的子控制器来处理业务逻辑
String code = action.execute(req, resp);//子控制器传来什么就是什么
ForwardModel forwardModel = actionModel.pop(code);//接收forward转发对象
if (forwardModel == null) {
throw new RuntimeException("你没有配置对应的子控制器Action的处理方式forward!!");
}
String jspPath = forwardModel.getPath();//通过name值得到路径
if (forwardModel.isRedirect()) {//如果redirect为true就重定向
resp.sendRedirect(req.getContextPath()+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();
}
}
}
在config.xml配置中之前太繁琐了,就一个就刚刚好
如果我们在其他文件调用比如
我们在中央控制器中的 this.getInitParameter(“xmlPath”); 起到作用
try {
// 将原有的读取框架的默认设置文件转变成读取可配置路径的配置文件
String xmlPath = this.getInitParameter("xmlPath");
if (xmlPath == null || "".equals(xmlPath)) {//如果调用是配置文件是空的,就用默认的
configModel = ConfigModelFactory.build();
}else {
configModel = ConfigModelFactory.build(xmlPath);
}
} catch (DocumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
在web.xml配置
最后可以试验我们加强版的MVC怎么样
如果redirect=“true” 的话我们写的处理业务逻辑的判断就起到作用了
<action path="/cal" type="com.c.web.CalAction">
<forward name="calRes" path="/calRes.jsp" redirect="true" />
</action>
注:Action多例模式是因为Action的属性要用来接收参数
思路:
- 1 将Action的信息配置到xml(反射实例化)
- 2 通过结果码控制页面的跳转
- 3 将一组相关的操作放到一个Action中(反射调用方法)
- (CalAction extends DispatcherAction
提供一组与execute方法的参数、返回值相同的方法,只有方法名不一样) - 4 利用ModelDriver接口对Java对象进行赋值(反射读写方法)
- 5 使得框架的配置文件可变