本期内容:
?
一、概念?
?为什么学习自定义MVC框架?
MVC怎么出现:各司其职
?MVC含义:模型层(model) 视图层(view) 控制层(controller)
? ? ? ? 以前问题:
????????????????1、数据处理Dao?? ??? ?(代码思路基本重复) ? ? ? ? ? ? ? ? 2、servlet?? ??? ?(代码思路基本重复) ? ? ? ? ? ? ? ? ? ? ? ①doget/dopost,实际上doget基本用不上 ? ? ? ? ? ? ? ? ? ?? ②实体类参数接受代码冗余(req.getParammeter(""),尤其当实体类属性多的情况下,封装到实体类中) ? ? ? ? ? ? ? ? ? ? ? ③关于结果页面的跳转(转发、重定向) ? ? ? ? ? ? ? ? ????????req.getdispather("/index.jsp").forward(req,resp); ? ? ? ? ? ? ? ? ? ????????resp.sendredirect("/index.jsp"); ? ? ? ? ? ? ? ? 3、jsp?? ?(代码思路基本重复HTML、js)?? ?
<!-- 绝大多数人开发采用这种方式做增删改查(servlet层) --> ?? ?<a href="${pageContext.request.contextPath }/book/add">新增</a> ?? ?<a href="${pageContext.request.contextPath }/book/delete">删除</a> ?? ?<a href="${pageContext.request.contextPath }/book/edit">修改</a> ?? ?<a href="${pageContext.request.contextPath }/book/list">查询</a>?
?每个方法还要写一个servlet:
第二种:
优点:相较于前一种,代码量减少,由原来的四个类变成一个类 缺点:每一次新增一个方法,都要改动原有逻辑,是代码过于冗余
?<!-- 少数人开发采用这种方式做增删改查(servlet层) --> ?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=add">新增</a> ?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=delete">删除</a> ?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=edit">修改</a> ?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=list">查询</a> ?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=load">回显</a>
?只要写一个servlet
package com.lv.servlet;
import java.io.IOException; import java.lang.reflect.Method;
import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/book.action") public class BookServlet extends HttpServlet{ ?? ?@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 {
?? ??? ?String methodName = req.getParameter("methodName"); ? ? ? ? if("add".equals(methodName)) { ? ? ? ? ? ? add(req,resp); ? ? ? ? }else if("edit".equals(methodName)) { ? ? ? ? ? ? edit(req,resp); ? ? ? ? }else if("delete".equals(methodName)) { ? ? ? ? ? ? delete(req,resp); ? ? ? ? }else if("list".equals(methodName)) { ? ? ? ? ? ? list(req,resp); ? ? ? ? }else if("load".equals(methodName)) { ? ? ? ? ? ? load(req,resp); ? ? ? ? }
?? ??? ?}
?? ?private void load(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.load()"); ?? ?}
?? ?private void list(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.list()"); ?? ?}
?? ?private void delete(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.delete()"); ?? ?}
?? ?private void edit(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.edit()"); ?? ?}
?? ?private void add(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.add()"); ?? ?} ?? ?private void ref(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.ref()"); ?? ?}
}
框架:反射+设计模式(极大的减少了代码量,把重复性的代码交给框架完成,让程序员关注点放在项目业务上)
????????1、通用分页指点+通用的增删改 ? ? ? ? 2、各层(主要体现在mc层)数据Dao层、控制层代码缩减 ? ? ? ? 3、前台代码的缩减优化
对以上第二种的解决方案: ? ? ? ? ? ? ? 调用哪一个方法,实际上是取决于methodName,加if不是必要条件 ? ? ? ? ? ? ? 直白一点,也就是说动态调用methodName方法,并且是当前类实例的methodName方法
<!-- 反射优化 --> ?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=add">新增</a> ?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=delete">删除</a> ?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=edit">修改</a> ?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=list">查询</a> ?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=load">回显</a>
每一次新增一个方法,不需要改动原有逻辑,只要在最下增加一个方法就好
package com.lv.servlet;
import java.io.IOException; import java.lang.reflect.Method;
import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @WebServlet("/book.action") public class BookServlet extends HttpServlet{ ?? ?@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 { ?? ??? ?String methodName = req.getParameter("methodName");
?? ??? ?try { ?? ??? ??? ?Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class); ?? ??? ??? ?m.setAccessible(true); ?? ??? ??? ?m.invoke(this,req, resp); ?? ??? ??? ? ?? ??? ?} catch (Exception e) { ?? ??? ??? ?// TODO Auto-generated catch block ?? ??? ??? ?e.printStackTrace(); ?? ??? ?}? ?? ??? ?}
?? ?private void load(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.load()"); ?? ?}
?? ?private void list(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.list()");? ?? ?}
?? ?private void delete(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.delete()"); ?? ?}
?? ?private void edit(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.edit()"); ?? ?}
?? ?private void add(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.add()"); ?? ?} ?? ?private void ref(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.ref()"); ?? ?}
} ?
总结: ? ? ? ? ? ? ? ? ? 反射可以修复上面改动代码才能解决需求问题的缺陷 ? ? ? ? ? ? ? ? ? 反射这段代码,相当于中央控制器,并不直接处理浏览器请求 ? ? ? ? ? ? ? ? ? 处理浏览器请求的是子控制器
二、完成自定义MVC框架
1、自定义MVC框架工作原理:
?2、中央控制器的优化及子控制器的优化
需解决的问题:
1.BookServlet中要做增删改查,那么必须要在doPost方法中写反射动态调用新增方法 ? ? ? ?? GoodsServlet要做增删改查,那么必须要在doPost方法中写反射动态调用新增方法 结论:反射动态调用新增方法代码是重复的的,但是这段又是必须的,也就是还需要进一步优化,优化中央控制器
2.对于BookServlet中要做增删改查而言,依然是每一个Servlet都要写doget/doPost方法 ? ? ? ? ? 但是实际上对处理业务有用的代码,往往是我们新加的,只需关注业务(add/delete/...) ? ? ? ? ?? 优化子控制器
ActionSupport类中放入放射动态调用方法:
package com.lv.framework;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
public class ActionSupport implements Action{
?? ?@Override ?? ?public String execute(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?String methodName = req.getParameter("methodName"); ?? ??? ?String res=null; ?? ??? ? ?? ??? ?try { ?? ??? ??? ?Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class); ?? ??? ??? ?m.setAccessible(true); ?? ??? ??? ?res=(String) m.invoke(this,req, resp); ?? ??? ??? ? ?? ??? ?} catch (Exception e) { ?? ??? ??? ?// TODO Auto-generated catch block ?? ??? ??? ?e.printStackTrace(); ?? ??? ?} ?? ??? ?return res;? ?? ?} } ?
BookAction相当于以前的BookServlet,从父类继承了execute方法,就把反射动态调用方法的代码继承过来了
package com.lv.framework;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
import com.lv.entity.Book;
public class BookAction extends ActionSupport implements ModelDriver<Book>{
?? ?private String add(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.add(book)"); ?? ??? ?return "list"; ?? ?} ?? ? ?? ?private void edit(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.edit()"); ?? ??? ? ?? ?} ?? ? ?? ?private String toEdit(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.toEdit()"); ?? ??? ?return "toEdit"; ?? ?} ?? ?private void list(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.list()"); ?? ??? ? ?? ?}
?? ?private void load(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.load()"); ?? ??? ? ?? ?}
?? ?@Override ?? ?public Book getModel() { ?? ??? ?// TODO Auto-generated method stub ?? ??? ?return book; ?? ?}
} ?
?我们现在还需要之前所讲的建模有关的知识,引用:
?config.xml文件中:
path内为跳转的界面路径,
false代表转发,true则为重定向
<?xml version="1.0" encoding="UTF-8"?> <config> ?? ?<!--? ?? ??? ?在这里每加一个配置,就相当于actions.put("/goods", new GoodsAction()); ?? ??? ?这样就解决了代码灵活性的问题 ?? ? --> ?? ?<action path="/book" type="com.lv.framework.BookAction"> ?? ??? ?<forward name="list" path="/bookList.jsp" redirect="false" /> ?? ??? ?<forward name="toEdit" path="/bookEdit.jsp" redirect="true" /> ?? ?</action> </config>
DispatchServlet中央控制器
package com.lv.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.lv.xml.ActionModel; import com.lv.xml.ConfigModel; import com.lv.xml.ConfigModelFactory; import com.lv.xml.ForwardModel;
//jsp跳转为/book.action/goods.action/order.action……都会跳转到这个类 @WebServlet("*.action") public class DispatchServlet extends HttpServlet{ //?? ?再当前中央控制器中必然会有所有子控制器的集合 //?? ?缺陷:如果有商品的增删改查-->意味着要改动代码-->代码的设计不够灵活 //?? ?思考:在不改动代码的情况下,中央控制器也能找到对应的子控制器去处理浏览器请求 //?? ?方案:把加子控制器的逻辑/动作,放到配置文件中完成(Dbutil改连接信息是放在代码中完成/现在是放在Properties) //?? ?放在配置文件中完成的好处在于:代码更加灵活,修改相关信息不用动代码 //?? ?private Map<String, ActionSupport> actions=new HashMap<String, ActionSupport>(); //?? ?ConfigModel对象又通过建模的知识,把所有的的配置信息给读取过来 ?? ?private ConfigModel configModel=null;?? ? //?? ?初始化所有的子控制器到当前的中央控制器中 ?? ??? ?public void init() throws ServletException { //?? ??? ??? ?在集合中就有了一个子控制器 //?? ??? ??? ? actions.put("/book", new BookAction()); //?? ??? ??? ? actions.put("/goods", new GoodsAction()); //?? ??? ??? ? …… ?? ??? ??? ? try { ?? ??? ??? ??? ?configModel=ConfigModelFactory.build(); ?? ??? ??? ?} catch (Exception 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 { //?? ??? ??? ?将子控制器与浏览器请求关联起来,寻找能够处理请求的子控制器 //?? ??? ?如何找到浏览器这个路径:http://localhost:8080/book.action?methodName=add-->BookAction.add(); ?? ??? ?/* ?? ??? ? * 思路 ?? ??? ? * 1.url-->/book ?? ??? ? * 2.通过/book字符串在actions找到BookAction ?? ??? ? * 3.调用BookAction的add,想要调用add,实际上只要统一调用execute就可以了 ?? ??? ? */ //?? ??? ?获取到浏览器的请求地址 ?? ??? ?String url = req.getRequestURI(); //?? ??? ?1.url-->/book ?? ??? ?url=url.substring(url.lastIndexOf("/"), url.lastIndexOf(".")); ?? ??? ? //?? ??? ?2.通过/book字符串在actions找到BookAction //?? ??? ?ActionSupport actrion = actions.get(url); //?? ??? ?原来在Map中寻找子控制器——>现在在配置文件中寻找子控制器 ?? ??? ?/** ?? ??? ? * 思路: ?? ??? ? * 1.通过/book找到对应的ActionModel对象 ?? ??? ? * 2.通过ActionModel对象拿到类的全路径名com.lv.framework.BookAction ?? ??? ? * 3.反射实例化对象 ?? ??? ? */ ?? ??? ?ActionModel actionModel = configModel.pop(url); //?? ??? ?拿到类的全路径名 ?? ??? ?String type = actionModel.getType(); ?? ??? ?ActionSupport action; ?? ??? ?try { ?? ??? ??? ?action = (ActionSupport) Class.forName(type).newInstance(); //?? ??? ??? ?完成实体类参数的封装 ?? ??? ??? ?if(action instanceof ModelDriver) { //?? ??? ??? ??? ?当前子控制器实现了模型驱动接口 ?? ??? ??? ??? ?ModelDriver m=(ModelDriver) action; //?? ??? ??? ??? ?Book/Goods…… ?? ??? ??? ??? ?Object bean=m.getModel(); //?? ??? ??? ??? ?所有的请求参数都在这,需要将所有的请求参数封装到Book/Goods…… ?? ??? ??? ??? ?BeanUtils.populate(bean, req.getParameterMap()); ?? ??? ??? ?} //?? ??? ??? ?执行业务逻辑 BookAction.add方法的返回值"list" ?? ??? ??? ?/** ?? ??? ??? ? * 1、书籍新增那么跳转书籍展示页面BookList.jsp 转发 ?? ??? ??? ? * 2、书籍编辑跳转编辑页面BookEdit.jsp 重定向 ?? ??? ??? ? */ ?? ??? ??? ?String res=action.execute(req, resp); ?? ??? ??? ?ForwardModel forwardModel=actionModel.pop(res); ?? ??? ??? ?String path=forwardModel.getPath(); ?? ??? ??? ?boolean isredirect = forwardModel.isRedirect(); ?? ??? ??? ?if(isredirect) { ?? ??? ??? ??? ?resp.sendRedirect(req.getContextPath()+path); ?? ??? ??? ?}else { ?? ??? ??? ??? ?req.getRequestDispatcher(path).forward(req, resp); ?? ??? ??? ?} ?? ??? ??? ? ?? ??? ?} catch (Exception e) { ?? ??? ??? ?// TODO Auto-generated catch block ?? ??? ??? ?e.printStackTrace(); ?? ??? ?} ? ? ? ? ? ? ? ? }
} ?
3、建立模型驱动接口
解决的问题:
实体类参数接受代码冗余(req.getParammeter(“”),尤其当实体类属性多的情况,封装到实体类中
如:
book.setBid(req.getParameter("bid")); book.setBname(req.getParameter("bname")); book.setPrice(req.getParameter("price")); book.setAthor(req.getParameter("athor")); book.setPublish(req.getParameter("publish"));
ModelDriver模型驱动接口:
package com.lv.framework;
/** ?* 模型驱动接口 ?* 作用:帮助“中央控制器”完成参数封装的工程 ?*/ public interface ModelDriver<T> { ?? ?/** ?? ? * GoodsAction-->goods ?? ? * BookAction-->book ?? ? * ... ?? ? * @return ?? ? */ ?? ?T getModel(); } ?
在中央控制器的操作:
//?? ?ConfigModel对象又通过建模的知识,把所有的的配置信息给读取过来 ?? ?private ConfigModel configModel=null;?? ? //?? ?初始化所有的子控制器到当前的中央控制器中 ?? ??? ?public void init() throws ServletException { //?? ??? ??? ?在集合中就有了一个子控制器 //?? ??? ??? ? actions.put("/book", new BookAction()); //?? ??? ??? ? actions.put("/goods", new GoodsAction()); //?? ??? ??? ? …… ?? ??? ??? ? try { ?? ??? ??? ??? ?configModel=ConfigModelFactory.build(); ?? ??? ??? ?} catch (Exception e) { ?? ??? ??? ??? ?// TODO Auto-generated catch block ?? ??? ??? ??? ?e.printStackTrace(); ?? ??? ??? ?} ?? ??? ?}
?中央控制器内中doPast:
//?? ??? ?2.通过/book字符串在actions找到BookAction //?? ??? ?ActionSupport actrion = actions.get(url); //?? ??? ?原来在Map中寻找子控制器——>现在在配置文件中寻找子控制器 ?? ??? ?/** ?? ??? ? * 思路: ?? ??? ? * 1.通过/book找到对应的ActionModel对象 ?? ??? ? * 2.通过ActionModel对象拿到类的全路径名com.lv.framework.BookAction ?? ??? ? * 3.反射实例化对象 ?? ??? ? */ ?? ??? ?ActionModel actionModel = configModel.pop(url); //?? ??? ?拿到类的全路径名 ?? ??? ?String type = actionModel.getType(); ?? ??? ?ActionSupport action; ?? ??? ?try { ?? ??? ??? ?action = (ActionSupport) Class.forName(type).newInstance(); //?? ??? ??? ?完成实体类参数的封装 ?? ??? ??? ?if(action instanceof ModelDriver) { //?? ??? ??? ??? ?当前子控制器实现了模型驱动接口 ?? ??? ??? ??? ?ModelDriver m=(ModelDriver) action; //?? ??? ??? ??? ?Book/Goods…… ?? ??? ??? ??? ?Object bean=m.getModel(); //?? ??? ??? ??? ?所有的请求参数都在这,需要将所有的请求参数封装到Book/Goods…… ?? ??? ??? ??? ?BeanUtils.populate(bean, req.getParameterMap()); ?? ??? ??? ?}
BookAction中实现ModelDriver<Book>:
package com.lv.framework;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
import com.lv.entity.Book;
public class BookAction extends ActionSupport implements ModelDriver<Book>{ //?? ?当前子控制器在哪里调用?把子控制器与浏览器请求关联起来 ?? ?public Book book=new Book(); ?? ?private String add(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println(book); ?? ??? ?System.out.println("bookDao.add(book)"); ?? ??? ?return "list"; ?? ?} ?? ?
?? ?@Override ?? ?public Book getModel() { ?? ??? ?// TODO Auto-generated method stub ?? ??? ?return book; ?? ?}
} ?
界面:?
输出结果:
?4、页面的跳转
子控制器:?处理浏览器请求,针对于add/ref/other进行向上抽取、抽象
package com.lv.framework;
import java.io.IOException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;
/** ?* 子控制器 ?* 针对于add/ref/other进行向上抽取、抽象 ?*/ @WebServlet("/Action") public interface Action{ ?? ?/*private void add(HttpServletRequest req, HttpServletResponse resp) { ?? ??? ?System.out.println("bookDao.edit()"); ?? ??? ? ?? ?}*/ //?? ?这个方法就是add/ref/other进行向上抽取的抽象方法 //?? ?作用:能够处理浏览器的“所有”请求,包括add/ref/other //?? ?通过返回值来决定跳转哪一个页面,至于重定向还是转发,由中央控制器决定 ?? ?public String execute(HttpServletRequest req, HttpServletResponse resp); ?? ? } ?
?中央控制器决定:
/** ?? ??? ??? ? * 1、书籍新增那么跳转书籍展示页面BookList.jsp 转发 ?? ??? ??? ? * 2、书籍编辑跳转编辑页面BookEdit.jsp 重定向 ?? ??? ??? ? */ ?? ??? ??? ?String res=action.execute(req, resp); ?? ??? ??? ?ForwardModel forwardModel=actionModel.pop(res); ?? ??? ??? ?String path=forwardModel.getPath(); ?? ??? ??? ?boolean isredirect = forwardModel.isRedirect();
// ? ? ? ? isredirect指是重定向 ?? ??? ??? ?if(isredirect) { ?? ??? ??? ??? ?resp.sendRedirect(req.getContextPath()+path); ?? ??? ??? ?}else { ?? ??? ??? ??? ?req.getRequestDispatcher(path).forward(req, resp); ?? ??? ??? ?} ?? ??? ??? ? ?? ??? ?} catch (Exception e) { ?? ??? ??? ?// TODO Auto-generated catch block ?? ??? ??? ?e.printStackTrace(); ?? ??? ?}
内容结束~~~
|