1.什么是MVC?
MVC全名:Model View Controller
是Model(模型层)、View(视图层)、Controller(控制层)的缩写
它是一种软件设计典范,用于业务逻辑处理、数据、界面显示分离
?
2.使用MVC的目的?
将M(业务模型)和V(用户界面)实现代码分离,从而使一个程序可以使用不同的表现形式,M(控制器)则确保M和V的同步,M改变,V随之更新
常用模式: model1:jsp+jdbc model2:mvc 实现高内聚 ?低耦合 ? 各司其职?
注意:1)不能跳层调用方法
? ? ? ? ? ?2)只能由上往下进行调用;View -> Controller -> Model
3.MVC的结构和工作原理
V(视图层) :JSP,HTML,freemarker
C(控制层) :Servlet,Action,Controller
M(模型层) :entity、dao
自定义MVC工作原理图
?
?举例:
我们之前写增删改查的时候,每写一个方法就有一个对应的servlet用来调用方法及获取数据,再传值给jsp界面,但是这样会产生很多重复的代码,降低开发效率。
<a href="index.do?name=add">增加</a> <a href="index.do?name=delete">删除</a> <a href="index.do?name=upd">修改</a> <a href="index.do?name=sele">查询</a>
方法的调用取决于name ,我们可以把所有方法放在一个servlet 中,通过获取name 的值控制方法的调用 ,但是如果有新的方法增加,我们还是要不断改动原来的代码,使代码过于冗余。
因此我们可以利用反射,不改动原有逻辑,通过读取name读取实现需求
package com.ltf.mvc.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("/index.do")
public class indexServlet extends HttpServlet{
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String a=req.getParameter("a");
// if("add".equals(a)) {
// add();
// }else if("del".equals(a)) {
// del();
// }else if("upd".equals(a)) {
// upd();
// }else if("find".equals(a)) {
// find();
//
// }
try {
Method m = this.getClass().getDeclaredMethod(a, HttpServletRequest.class,HttpServletResponse.class);
m.setAccessible(true);
m.invoke(this,req,resp);
} catch (Exception e) {
e.printStackTrace();
}
}
private void find(HttpServletRequest req, HttpServletResponse resp) {
// TODO Auto-generated method stub
System.out.println("查询。。。。。。。。。");
}
private void upd(HttpServletRequest req, HttpServletResponse resp) {
// TODO Auto-generated method stub
System.out.println("修改。。。。。。。。。");
}
private void del(HttpServletRequest req, HttpServletResponse resp) {
// TODO Auto-generated method stub
System.out.println("删除。。。。。。。。。");
}
private void add(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("增加。。。。。。。。。");
}
private void ref(HttpServletRequest req, HttpServletResponse resp) {
System.out.println("ref()。。。。。。。。。");
}
}
利用放射,可以解决改动代码的缺陷
反射这段代码相当于中央控制器,并不直接处理浏览器请求
处理浏览器请求的是子控制器
那么 我们来思考一个问题:例如 book要进行增删改查,goods也要进行增删改查,那么在bookServlet和goodsServlet都要写反射,一旦需求多了起来,反射代码会变得重复繁多,但是它又是必须的,我们可以调整这段代码的位置,也就是说还需要进一步优化
Action这一接口的作用:针对add、del进行向上抽取的抽象方法
package com.ltf.framework;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 子控制器
* 处理浏览器请求
* 针对于add/ref/other进行向上抽取、抽象 abstract
*/
public interface Action {
public String execute(HttpServletRequest req, HttpServletResponse resp);
}
?ActionSupport?
实现Action 重写execte方法 ,通过反射获取对应方法(处理所有请求)
作用:当出现更多需求时,只需要继承?ActionSupport?类即可
package com.ltf.framework;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
/**
* 作用:能够处理浏览器的“所有”请求。包括add/ref/other...
*/
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;
}
}
?DispatchServlet (中央控制器)?
package com.ltf.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 DispathServlet extends HttpServlet {
// 在当前中央控制器中必然会有所有子控制器的集合
// 缺陷:如果有商品的增删改查-->意味着要改动代码-->代码的设计不够灵活
// 思考:在不改动代码的情况下,中央控制器也能找到对应的子控制器去处理浏览器请求
// 方案:把加子控制器的逻辑/动作,放到配置文件中完成(Dbutil改连接信息是放在代码中完成/现在是放在Properties文件中完成)
// 放在配置文件中完成的好处在于:代码更加灵活,修改相关信息不用动代码了
// private Map<String, ActionSupport> actions = new HashMap<>();
// configModel对象又通过建模的知识,把所有的配置信息给读取过来了
private ConfigModel configModel = null;
/**
* 初始化所有的子控制器到当前的中央控制器中
*/
public void init() throws ServletException {
// 在集合中就有了一个子控制器
// actions.put("/book", new BookAction());
// actions.put("/goods", new GoodsAction());
// actions.put("/order", new OrderAction());
// ...
try {
configModel = ConfigModelFactory.build();
} catch (Exception e) {
e.printStackTrace();
}
}
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
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();
// url-->/book
url = url.substring(url.lastIndexOf("/"), url.lastIndexOf("."));
// 通过/book字符串在actions找到BookAction
// ActionSupport actrion = actions.get(url);
// 在Map中寻找子控制器-->在配置文件中寻找子控制器
/*
* 1.通过/book找到对应的ActionModel对象 2.通过ActionModel对象拿到类的全路径名com.zking.web.BookAction
* 3.反射实例化对象
*/
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”
/*
* 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) {
e.printStackTrace();
}
}
}
中央控制器作用:1.获取浏览器请求URL 2. 寻找可以处理请求的子控制器
在中央控制器中一定会存在一个储存着所有子控制器的集合 将所有子控制器初始化到中央控制器中 我们发现子控制器集合放在中央控制器中,当有了新的需求时,需要将子控制器添加到集合中,就
是说需要改的代码。那么我们将子控制器放到配置文件中,在初始化时,利用xml建模 将配置文件中
的子控制器读取出来,这样无论有多少需求,我们只需要在配置文件中配置即可。 ?
实体类参数接受代码冗余(req.getparameter(""))
优化方法:
模型驱动接口? 作用:
帮助中央控制器完成参数封装的工程
跳转界面的优化:
config文件:
<action path="book" type="com.ltf.web.BookAction">
<forward name="list" path="booklist.jsp" redirect="false" />
<forward name="toEdit" path="toEdit.jsp" redirect="true" />
</action>
path为跳转路径,name为调用方法,redirect=false为转发,反之重定向
修改execute方法为有返回
|