目录
一、MVC简介
1.什么是MVC?
2、三层架构和MVC的区别?
3、MVC结构?
二、框架:演绎过程
1、之前的开发模式
①、 绝大多数人采用这种方法做增删改查,需要多少个servlet就建多少
②、少数人采用这种方法做增删改查(select层)
2、反射优化
三、自定义MVC框架
1、自定义MVC工作原理图
2、优化中央控制器、优化子控制器
①、使用到xml建模
②、jar包
③、中央控制器(DispatchServlet )
④、子控制器(Action接口)
⑤、ActionSupport(实现Action接口)
⑥、BookAction (继承ActionSupport)
3、实体类参数接受代码冗余
①、Book(实体类)
②、DispatchServlet
③、ModelDriver
④、 BookAction (extends ActionSupport implements ModelDriver)
4、关于结果页面的跳转(转发、重定向)
①、中央控制器
②、子控制器
③、ActionSupport
④、BookAction
⑤、xml
MVC全名:Model View Controller,其中Model(模型层)、View(视图层)、Controller(控
层)它是一种软件设计典范,用于业务逻辑处理、数据、界面显示分离,
三层架构是一个经典的分层思想,将开发模式分为三层,每个人专注自己擅长模块即可
MVC是一种设计模式,其目的是让视图和业务逻辑分开
①、 V(视图层) -->JSP/HTML/freemarker
②、 C(控制层) -->Servlet/Action/Controller
③、M(模型层) -->entity、dao
注:(1)不能跨层调用;
(2)只能由上往下进行调用;View -> Controller -> Model
package com.mwy.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层)
新增
删除
修改
查询
package com.mwy.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层)
新增
删除
修改
查询
package com.mwy.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 {
/**
* 优点:相交于前一种,代码量时间少的,由原来4个类变成了1个类
* 缺点:
* 每一次新增一个方法,都要改动原有逻辑,使代码过于冗余
* 举例:一般当修改的时候,需要做数据回显load
* 思考:
* 不改动原有逻辑,也能实现需求
* 解决方案:
* 调用哪个方法,实际上是取决于methodName,加if不是必要条件
* 直白一点:动态调用methodName方法,并且是当前类实例的methodName方法
* 总结:
* 反射可以修复上面改动代码才能解决需求问题缺陷
* 反射这段代码,相当于中央控制器,并不直接处理浏览器请求
* 处理浏览器请求的是子控制器
*/
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界面
反射优化
回显
关联查询
图中:
*.action 调度 截取*(请求路径名) 处理具体业务逻辑
JSP ---------> Servlet(中央控制器)--------------------->Action(子控制器)--->Model(Dao、DB)
package com.mwy.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 org.apache.commons.beanutils.PropertyUtils;
import org.dom4j.DocumentException;
import com.mwy.servlet.BookAction;
import com.mwy.servlet.GoodsAaction;
/**
* 中央控制器
*jsp:/book.action/goods.action
*
*/
@WebServlet("*.action")
public class DispatchServlet extends HttpServlet{
//在当前中央控制器中必然会有所有子控制器集合
//缺陷:如果有商品的增删改查-->意味着要改动代码-->代码的设计不够灵活
//思考:在不改动代码的情况下,中央控制器也能找到对应的子控制器去处理浏览器请求
//方案:我把加子控制器的逻辑/动作,放到配置文件中完成(Dbutil改连接信息是放在代码中完成/现在是放在Properties文件中完成)
//放在配置文件中完成的好处在于代码更加灵活,修改相关信息不用动代码
//ConfigModel对象又通过建模的知识,把所有的配置信息给读取过来了
//private Map actions=new HashMap<>();
//现在在xml中改
private ConfigModel configModel=null;
/**
* 初始化所有的子控制器到当前的中央控制器中
*/
@Override
public void init() throws ServletException {
//在集合中就有了一个子控制器
//actions.put("/book", new BookAction());
//缺陷显示
//actions.put("/goods",new GoodsAaction());
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 {
//把子控制器与浏览器请求关联起来,“寻找”能够处理请求的子控制器
/**
* 思路
* 1、uri-->book
* 2、通过/book字符串在actions找到BookAction
* 3、调用BookAction的add,想要调用add,实际上只要统一调用execute就可以了
*/
//获取到浏览器的请求地址
String uri = req.getRequestURI();
uri = uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf("."));
//通过/book字符串在actions找到BookAction
//ActionSupport action = actions.get(uri);
//原来在Map中寻找子控制器-->在配置文件中寻找子控制器
/**
* 1、通过/book找到对应的ActionModel对象
* 2、通过ActionModel对象拿到类的全路径名com.mwy.servlet.BookAction
* 3、反射实例化对象
*/
ActionModel actionModel = configModel.pop(uri);
String type = actionModel.getType();
ActionSupport action;
try {
action = (ActionSupport) Class.forName(type).newInstance();
action.execute(req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
//不需要在代码中动,在xml中动
package com.mwy.framework;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 子控制器
*他来处理浏览器请求
*针对于add/ref进行向上抽取、抽象 abstract
*/
public interface Action {
//这个方法就是add/ref进行向上抽取的方法
//作用:能够处理浏览器的“所有”请求,包括add/ref
public void execute(HttpServletRequest req,HttpServletResponse resp);
// private void add(HttpServletRequest req, HttpServletResponse resp) {
// System.out.println("--增加--");
//
// }
//
// private void ref(HttpServletRequest req, HttpServletResponse resp) {
// System.out.println("--ref--");
//
// }
}
package com.mwy.framework;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 作用:能够处理浏览器的“所有”请求,包括add/ref
*
*/
public class ActionSupport implements Action{
@Override
public void execute(HttpServletRequest req, HttpServletResponse resp) {
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();
}
}
}
package com.mwy.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.mwy.framework.ActionSupport;
public class BookAction extends ActionSupport{
//从父类继承了execute方法,就把反射动态调用方法的代码继承过来了
//BookAction-->BookServlet
//BookAction/GoodsAction/OrderAction...
//当前子控制器在哪里调用?把子控制器与浏览器请求关联起来
//以下只需要关注业务
private void add(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("--增加--");
}
private void list(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("--查询--");
}
private void ref(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("--ref--");
}
private void goods(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("--购物--");
}
}
package com.mwy.entity;
public class Book {
private String bid;
private String bname;
private String price;
private String athor;
private 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 + "]";
}
}
package com.mwy.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 org.apache.commons.beanutils.PropertyUtils;
import org.dom4j.DocumentException;
import com.mwy.servlet.BookAction;
import com.mwy.servlet.GoodsAaction;
/**
* 中央控制器
*jsp:/book.action/goods.action
*/
@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 uri = req.getRequestURI();
uri = uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf("."));
ActionModel actionModel = configModel.pop(uri);
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());
//PropertyUtils.getProperty(bean, name);
}
action.execute(req, resp);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.mwy.framework;
import org.apache.commons.beanutils.BeanUtils;
import com.mwy.entity.Book;
/**
* 模型驱动接口作用,帮助中央控制器完成参数封装工程
* BeanUtils.populate(bean, req.getParameterMap());相当于下面代码
* Book book=new Book();
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"));
* @param
*/
public interface ModelDriver {
/**
* GoodsAction-->goods
* BookAction-->book
* @return
*/
T getModel();
}
package com.mwy.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.mwy.entity.Book;
import com.mwy.framework.ActionSupport;
import com.mwy.framework.ModelDriver;
public class BookAction extends ActionSupport implements ModelDriver{
//从父类继承了execute方法,就把反射动态调用方法的代码继承过来了
//BookAction-->BookServlet
//BookAction/GoodsAction/OrderAction...
//当前子控制器在哪里调用?把子控制器与浏览器请求关联起来
Book book=new Book();
//以下只需要关注业务
private void 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("--增加--");
}
@Override
public Book getModel() {
// TODO Auto-generated method stub
return book;
}
}
package com.mwy.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 org.apache.commons.beanutils.PropertyUtils;
import org.dom4j.DocumentException;
import com.mwy.servlet.BookAction;
import com.mwy.servlet.GoodsAaction;
/**
* 中央控制器
*jsp:/book.action/goods.action
*
*/
@WebServlet("*.action")
public class DispatchServlet extends HttpServlet{
//在当前中央控制器中必然会有所有子控制器集合
//缺陷:如果有商品的增删改查-->意味着要改动代码-->代码的设计不够灵活
//思考:在不改动代码的情况下,中央控制器也能找到对应的子控制器去处理浏览器请求
//方案:我把加子控制器的逻辑/动作,放到配置文件中完成(Dbutil改连接信息是放在代码中完成/现在是放在Properties文件中完成)
//放在配置文件中完成的好处在于代码更加灵活,修改相关信息不用动代码
//ConfigModel对象又通过建模的知识,把所有的配置信息给读取过来了
//private Map actions=new HashMap<>();
private ConfigModel configModel=null;
/**
* 初始化所有的子控制器到当前的中央控制器中
*/
@Override
public void init() throws ServletException {
//在集合中就有了一个子控制器
//actions.put("/book", new BookAction());
//缺陷显示
//actions.put("/goods",new GoodsAaction());
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 {
//把子控制器与浏览器请求关联起来,“寻找”能够处理请求的子控制器
/**
* 思路
* 1、uri-->book
* 2、通过/book字符串在actions找到BookAction
* 3、调用BookAction的add,想要调用add,实际上只要统一调用execute就可以了
*/
//获取到浏览器的请求地址
String uri = req.getRequestURI();
uri = uri.substring(uri.lastIndexOf("/"), uri.lastIndexOf("."));
//通过/book字符串在actions找到BookAction
//ActionSupport action = actions.get(uri);
//原来在Map中寻找子控制器-->在配置文件中寻找子控制器
/**
* 1、通过/book找到对应的ActionModel对象
* 2、通过ActionModel对象拿到类的全路径名com.mwy.servlet.BookAction
* 3、反射实例化对象
*/
ActionModel actionModel = configModel.pop(uri);
String type = actionModel.getType();
ActionSupport action;
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";
/**
* 1、书籍新增那么跳转书籍展示页面BookList.jsp 转发
* 2、书籍编辑跳转编辑界面BookEdit.jsp 重定向
*/
String res = action.execute(req, resp);
ForwardModel fm = actionModel.pop(res);
String path = fm.getPath();
boolean isRedirect = fm.isRedirect();
if(isRedirect) {
resp.sendRedirect(req.getContextPath()+path);
}else {
req.getRequestDispatcher(path).forward(req, resp);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.mwy.framework;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 子控制器
*他来处理浏览器请求
*针对于add/ref进行向上抽取、抽象 abstract
*/
public interface Action {
//这个方法就是add/ref进行向上抽取的方法
//作用:能够处理浏览器的“所有”请求,包括add/ref
//通过返回值来决定跳转哪一个页面(至于是重定向/转发由中央控制器来决定)
public String execute(HttpServletRequest req,HttpServletResponse resp);
}
package com.mwy.framework;
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 作用:能够处理浏览器的“所有”请求,包括add/ref
*/
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;
}
}
package com.mwy.servlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.mwy.entity.Book;
import com.mwy.framework.ActionSupport;
import com.mwy.framework.ModelDriver;
public class BookAction extends ActionSupport implements ModelDriver{
//以下只需要关注业务
private String add(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("--增加--");
return "list";
}
private String toEdit(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("--toEdit--");
return "toEdit";
}
@Override
public Book getModel() {
// TODO Auto-generated method stub
return book;
}
}