本节知识总结:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiI2EzX4xSZz91ZsAzNfRHLGZkRGZkRfJ3bs92YsAjMfVmepNHLwMVY1QUZ2UjTpxUNkZDT1A1QiVTQClGVF5UMR9Fd4VGdsATNfd3bkFGazxycykFaKdkYzZUbapXNXlleSdVY2pESa9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnL4ADO4QTNxUTMxMDOwEjMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
目录
本节知识总结:
一、mvc概念及思想:
二、自定义MVC演绎过程及原理
三、MVC模式和以前学习的模式(三层架构)对比:
1.几个方法写几个servlet:
2.然后就会有人用到select层,传值:
3.反射优化 :
四、自定义MVC的实现 优化
1、中央控制器和子控制器的优化(需要用到之前的XML建模):
2、建立模型驱动接口(实体类参数接受代码冗余):
3、关于结果页面的跳转(转发、重定向):
一、mvc概念及思想:
MVC全称 Model View Controller,是模型(Model)-视图(View)-控制器(Controller)的缩写,是一种软件设计思想。
MVC的核心思想:将代码按照不同的作用进行划分
具体划分原则是:
Model:处理大部分的业务逻辑和数据操作JSP/HTML/freemarker
View:负责渲染数据,以html的方式呈现给用户Servlet/Action/Controller
Controller:处理用户交互,负责从视图读取数据,控制用户输入,并向模型发送数据entity、dao
作用:MVC不能提升程序执行效率,但是会提升开发效率,提高代码的重用,对于后期的更新升级具有莫大的好处
二、自定义MVC演绎过程及原理
(1)不能跨层调用;
(2)只能由上往下进行调用;View -> Controller -> Model
MVC图解:
自定义MVC工作原理:
注:ActionServlet-->DispatchServlet(Springmvc)
.action 调度 截取*(请求路径名) 处理具体业务逻辑
JSP ---> Servlet(中央控制器)--->Action(子控制器)--->Model(Dao、DB)
三、MVC模式和以前学习的模式(三层架构)对比:
1.几个方法写几个servlet:
代码:
package com.xhy.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/book/add")
public class AddBookServlet 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 {
System.out.println("--Add增加--");
}
}
//jsp界面
绝大多数人采用这种方法做增删改查(select层)
<a href="${pageContext.request.contextPath }/book/add" target="_blank" rel="external nofollow" >新增</a>
<a href="${pageContext.request.contextPath }/book/del" target="_blank" rel="external nofollow" >删除</a>
<a href="${pageContext.request.contextPath }/book/upd" target="_blank" rel="external nofollow" >修改</a>
<a href="${pageContext.request.contextPath }/book/list" target="_blank" rel="external nofollow" >查询</a>
2.然后就会有人用到select层,传值:
相交于前一种,代码量时间少的,由原来4个类变成了1个类
缺点: 每一次新增一个方法,都要改动原有逻辑,使代码过于冗余
举例:一般当修改的时候,需要做数据回显load
代码:
package com.xhy.web;
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("del".equals(methodName)){
del(req,resp);
}else if("upd".equals(methodName)){
upd(req,resp);
}else if("list".equals(methodName)){
list(req,resp);
}else if("load".equals(methodName)){
load(req,resp);
}
}
private void list(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("--查询--");
}
private void upd(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("--修改--");
}
private void del(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("--删除--");
}
private void add(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("--增加--");
}
//jsp界面
少数人采用这种方法做增删改查(select层)
<a href="${pageContext.request.contextPath }/book.action?methodName=add" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >新增</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=del" target="_blank" rel="external nofollow" >删除</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=upd" target="_blank" rel="external nofollow" >修改</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=list" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >查询</a>
3.反射优化 :
- 反射可以修复上面改动代码才能解决需求问题缺陷
- 反射这段代码,相当于中央控制器,并不直接处理浏览器请求
- 处理浏览器请求的是子控制器
package com.xhy.web;
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) {
e.printStackTrace();
}
}
private void load(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("--回显--");
}
private void ref(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("--ref--");
}
}
//jsp界面反射优化
<a href="${pageContext.request.contextPath }/book.action?methodName=load" target="_blank" rel="external nofollow" >回显</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=ref" target="_blank" rel="external nofollow" >关联查询</a>
1、2、3显示界面:
利用放射,可以修复改动代码的缺陷
反射这段代码相当于中央控制器,并不直接处理浏览器请求
处理浏览器请求的是子控制器
所以有了以下优化:
四、自定义MVC的实现 优化
1、中央控制器和子控制器的优化(需要用到之前的XML建模):
参考:XML建模
分析:
在当前中央控制器中必然会有所有子控制器集合
//缺陷:如果有商品的增删改查-->意味着要改动代码-->代码的设计不够灵活
//思考:在不改动代码的情况下,中央控制器也能找到对应的子控制器去处理浏览器请求
//方案:我把加子控制器的逻辑/动作,放到配置文件中完成(Dbutil改连接信息是放在代码中完成/现在是放在Properties文件中完成)
//放在配置文件中完成的好处在于代码更加灵活,修改相关信息不用动代码
//ConfigModel对象又通过建模的知识,把所有的配置信息给读取过来了
//private Map<String, ActionSupport> actions=new HashMap<>();
//现在在xml中改
①浏览所需jar包:
②中央控制器DispathServlet:
package com.xhy.framework;
import java.io.IOException;
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;
@WebServlet("*.action")
public class DispatchServlet extends HttpServlet {
private ConfigModel configModel = null;
@Override
public void init() throws ServletException {
try {
configModel = ConfigModelFactory.build();
} 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 {
// 把子控制器与浏览器请求关联起来,"寻找"能够处理请求的子控制器
// 获取到浏览器的请求地址
String url = req.getRequestURI();
// url-->/book
url = url.substring(url.lastIndexOf("/"), url.lastIndexOf("."));
// 通过/book字符串在actions找到BookAction
// ActionSupport actrion = actions.get(url);
// 在Map中寻找子控制器-->在配置文件中寻找子控制器
ActionModel actionModel = configModel.pop(url);
// 类的全路径名
String type = actionModel.getType();
ActionSupport action = null;
try {
// BookAction/GoodsAction/..
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());
// PropertyUtils.getProperty(bean, name)
}
// 执行业务逻辑 bookAction.add方法的返回值 “list”
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) {
e.printStackTrace();
}
}
}
config.xml :
<?xml version="1.0" encoding="UTF-8"?>
<config>
<!--
在这里每加一个配置,就相当于actions.put("/goods", new GoodsAction());
这样就解决了代码灵活性的问题
-->
<action path="/book" type="com.xhy.web.BookAction">
<forward name="list" path="/bookList.jsp" redirect="false" />//转发
<forward name="toEdit" path="/bookEdit.jsp" redirect="true" />//重定向
</action>
</config>
④子控制器(Action 接口):
package com.xhy.framework;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface Action {
//这一个方法就是add/ref/other进行向上抽取的抽象方法
// 作用:能够处理浏览器的“所有”请求。包括add/ref/other...
public void execute(HttpServletRequest req, HttpServletResponse resp);
}
⑤ActionSupport(实现Action):
package com.xhy.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) {
e.printStackTrace();
}
return res;
}
}
⑥BookAction(继承ActionSupport) :
package com.xhy.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.xhy.entity.Book;
import com.xhy.framework.ActionSupport;
import com.xhy.framework.ModelDriver;
public class BookAction extends ActionSupport implements ModelDriver<Book>{
// 从父类继承了execute方法,就把反射动态调用方法的代码继承过来了
// BookAction-->BookServlet
// BookAction/GoodsAction/OrderAction...
// 当前自动控制器在哪里调用?把子控制器与浏览器请求关联起来
public Book book = new Book();
private String add(HttpServletRequest req, HttpServletResponse resp) {
System.out.println(book);
System.out.println("bookDao.add(book)...");
return "list";
}
private void list(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("bookDao.list()...");
}
private String toEdit(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("bookDao.toEdit()...");
return "toEdit";
}
private void delete(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("bookDao.delete()...");
}
@Override
public Book getModel() {
return book;
}
}
中央控制器及子控制器优化
<a href="${pageContext.request.contextPath }/book.action?methodName=add" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >新增</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=list" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >查询</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=delete" target="_blank" rel="external nofollow" >删除</a>
<hr>
2、建立模型驱动接口(实体类参数接受代码冗余):
①实体book类:
package com.xhy.entity;
public class Book {
public String bid;
public String bname;
public String price;
public String athor;
public String publish;
public String getBid() {
return bid;
}
public void setBid(String bid) {
this.bid = bid;
}
public String getBname() {
return bname;
}
public void setBname(String bname) {
this.bname = bname;
}
public String getPrice() {
return price;
}
public void setPrice(String price) {
this.price = price;
}
public String getAthor() {
return athor;
}
public void setAthor(String athor) {
this.athor = athor;
}
public String getPublish() {
return publish;
}
public void setPublish(String publish) {
this.publish = publish;
}
@Override
public String toString() {
return "Book [bid=" + bid + ", bname=" + bname + ", price=" + price + ", athor=" + athor + ", publish="
+ publish + "]";
}
}
②DispatchServlet 中央控制器
package com.xhy.framework;
import java.io.IOException;
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;
@WebServlet("*.action")
public class DispatchServlet extends HttpServlet {
@Override
public void init() throws ServletException {
try {
configModel = ConfigModelFactory.build();
} 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 {
// 把子控制器与浏览器请求关联起来,"寻找"能够处理请求的子控制器
// 获取到浏览器的请求地址
String url = req.getRequestURI();
// url-->/book
url = url.substring(url.lastIndexOf("/"), url.lastIndexOf("."));
// 通过/book字符串在actions找到BookAction
// ActionSupport actrion = actions.get(url);
// 在Map中寻找子控制器-->在配置文件中寻找子控制器
ActionModel actionModel = configModel.pop(url);
// 类的全路径名
String type = actionModel.getType();
ActionSupport action = null;
try {
// BookAction/GoodsAction/..
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());
// PropertyUtils.getProperty(bean, name)
}
// 执行业务逻辑 bookAction.add方法的返回值 “list”
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) {
e.printStackTrace();
}
}
}
③、ModelDriver<T>(模型驱动接口)
package com.xhy.framework;
public interface ModelDriver<T> {
T getModel();
}
④、 BookAction (extends ActionSupport implements ModelDriver<Book>)
package com.xhy.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.xhy.entity.Book;
import com.xhy.framework.ActionSupport;
import com.xhy.framework.ModelDriver;
public class BookAction extends ActionSupport implements ModelDriver<Book>{
// 从父类继承了execute方法,就把反射动态调用方法的代码继承过来了
// BookAction-->BookServlet
// BookAction/GoodsAction/OrderAction...
// 当前自动控制器在哪里调用?把子控制器与浏览器请求关联起来
public Book book = new Book();
private String add(HttpServletRequest req, HttpServletResponse resp) {
// 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"));
System.out.println(book);
System.out.println("bookDao.add(book)...");
return "list";
}
private void list(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("bookDao.list()...");
}
private String toEdit(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("bookDao.toEdit()...");
return "toEdit";
}
private void delete(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("bookDao.delete()...");
}
@Override
public Book getModel() {
return book;
}
}
重现参数处理代码冗余的问题
<form action="${pageContext.request.contextPath }/book.action?methodName=add" method="post">
书籍ID:<input type="text" name="bid" value="2">
书籍名称:<input type="text" name="bname" value="2x">
书籍价格:<input type="text" name="price" value="2a">
书籍作者:<input type="text" name="athor" value="2b">
书籍出版社:<input type="text" name="publish" value="2c">
<input type="submit">
</form>
运行结果:
3、关于结果页面的跳转(转发、重定向):
由于经常要写到类似于:
* req.getdispather("/index.jsp").forward(req,resp);
* resp.sendredirect("/index.jsp");
通过修改Action(子控制器)返回值viod为String:
package com.xhy.framework;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public interface Action {
//这一个方法就是add/ref/other进行向上抽取的抽象方法
// 作用:能够处理浏览器的“所有”请求。包括add/ref/other...
// 通过返回值来决定跳转哪一个页面(至于是重定向/转发由中央控制器决定..)
public String execute(HttpServletRequest req, HttpServletResponse resp);
}
ActionSupport添加一个返回值:
package com.xhy.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) {
e.printStackTrace();
}
return res;
}
}
BookAction的返回值也要修改成String:
package com.xhy.web;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.xhy.entity.Book;
import com.xhy.framework.ActionSupport;
import com.xhy.framework.ModelDriver;
public class BookAction extends ActionSupport implements ModelDriver<Book>{
// 从父类继承了execute方法,就把反射动态调用方法的代码继承过来了
// BookAction-->BookServlet
// BookAction/GoodsAction/OrderAction...
// 当前自动控制器在哪里调用?把子控制器与浏览器请求关联起来
public Book book = new Book();
private String add(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("bookDao.add(book)...");
return "list";
}
private void list(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("bookDao.list()...");
}
private String toEdit(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("bookDao.toEdit()...");
return "toEdit";
}
private void delete(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("bookDao.delete()...");
}
@Override
public Book getModel() {
return book;
}
}
举例建立bookEdit.jsp、bookList.jsp
config.xml:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<!--
在这里每加一个配置,就相当于actions.put("/goods", new GoodsAction());
这样就解决了代码灵活性的问题
-->
<action path="/book" type="com.xhy.web.BookAction">
<forward name="list" path="/bookList.jsp" redirect="false" />//转发
<forward name="toEdit" path="/bookEdit.jsp" redirect="true" />//重定向
</action>
</config>
DispatchServlet:
package com.xhy.framework;
import java.io.IOException;
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;
@WebServlet("*.action")
public class DispatchServlet extends HttpServlet {
@Override
public void init() throws ServletException {
try {
configModel = ConfigModelFactory.build();
} 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 {
String url = req.getRequestURI();
url = url.substring(url.lastIndexOf("/"), url.lastIndexOf("."));
ActionModel actionModel = configModel.pop(url);
String type = actionModel.getType();
ActionSupport action = null;
try {
action = (ActionSupport) Class.forName(type).newInstance();
if(action instanceof ModelDriver) {
ModelDriver m = (ModelDriver) action;
Object bean = m.getModel();
BeanUtils.populate(bean, req.getParameterMap());
}
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) {
e.printStackTrace();
}
}
}
解决结果码页面跳转代码冗余问题
<a href="${pageContext.request.contextPath }/book.action?methodName=add" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" target="_blank" rel="external nofollow" >新增</a>
<a href="${pageContext.request.contextPath }/book.action?methodName=toEdit" target="_blank" rel="external nofollow" >去往编辑界面</a>
输出结果: