1.认识MVC
1.1MVC的定义
MVC是一种软件架构模式,它代表了Model-View-Controller(模型-视图-控制器)的缩写。MVC模式将应用程序分为三个主要组件:
- 模型(Model): 模型代表应用程序的数据和业务逻辑。它负责数据的存储、检索和处理,以及定义应用程序的行为。
- 视图(View): 视图负责显示模型的数据给用户。它展示了用户界面的外观和布局,并在需要时将用户的输入传递给控制器。
- 控制器(Controller): 控制器处理用户的输入并更新模型和视图。它接收用户的操作和命令,并根据这些输入来调整模型的状态,并更新视图来反映这些状态的变化。
1.2为什么要用MVC
使用MVC架构有以下几个好处:
- 分离关注点(Separation of Concerns):MVC将应用程序分解为模型、视图和控制器,使每个组件负责不同的任务。
- 可扩展性和可维护性(Scalability and Maintainability):MVC模式将应用程序的不同部分解耦,使得修改或扩展其中一个组件不会对其他组件产生影响。
- 代码重用(Code Reusability):MVC模式鼓励将数据处理和业务逻辑放在模型中,这样可以将这些部分抽象出来并在不同的应用场景中重用。
- 可测试性(Testability):由于MVC的组件之间的松散耦合,可以更容易地对每个组件进行单元测试。
- 支持并行开发(Support for parallel development):MVC模式允许开发团队独立工作在不同的组件上,因为每个组件都有清晰的职责和接口定义。
1.3MVC框架与三层架构的区别
MVC框架和三层架构是软件开发中常用的两种架构模式,它们有一些区别:
①组织方式:
- MVC框架是一种架构模式,它提供了一种组织和结构化应用程序的方法。它将应用程序分为模型、视图和控制器三个组件,每个组件有独立的职责。
- 三层架构是一种架构模式,它将应用程序分为表示层、业务逻辑层和数据访问层三个层级,每个层级有独立的职责。
②关注点:
- MVC框架的重点在于分离关注点(Separation of Concerns),通过对应用程序的数据、逻辑和展示的分离,实现代码的分层和解耦,使得团队成员能够更好地协作开发和维护应用程序。
- 三层架构的重点在于将应用程序的不同功能分配到不同的层级上,通过层级的划分实现业务逻辑的解耦和重用,以实现灵活性和可扩展性。
③使用方式:
- MVC框架是一种开发模式,可以从头开始自行开发,也可以使用现有的MVC框架,如Spring MVC、ASP.NET MVC等。这些框架提供了一套规范和工具,使开发者能够更快速、高效地构建MVC架构的应用程序。
- 三层架构是一种架构模式,它并没有特定的框架或工具概念。开发者需要自行设计和实现应用程序的不同层级,并定义它们之间的接口和交互方式。
④主要应用场景:
- MVC框架适用于各种规模的应用程序,并且在Web开发领域得到广泛应用。它可以实现良好的代码组织和可维护性,同时支持前后端分离、RESTful API等现代开发需求。
- 三层架构适用于大型复杂的应用程序,尤其是企业级应用程序。它通过层级的划分,实现可扩展性和可维护性,同时支持多种技术栈的组合和集成。
2.MVC三层架构代码的演化
下面我们写三个不同版本的代码来演示一下MVC三层架构代码的演化:
2.1版本一
index.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <p>版本1</p> 弊端:每一张表对应的每一个操作,都要写一个servlet类来处理<hr> <a href="bookAdd.action">增加</a> <a href="bookDel.action">删除</a> <a href="bookUpd.action">修改</a> <a href="bookList.action">查询</a> <hr><hr> </body> </html>
四个Servlet类:
BookListServlet:
package com.Kissship.web; 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; /** * Servlet implementation class BookListServlet */ @WebServlet("/bookList.action") public class BookListServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("BookListServlet.List..."); } }
BookDelServlet:
package com.Kissship.web; 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; /** * Servlet implementation class BookDelServlet */ @WebServlet("/bookDel.action") public class BookDelServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("BookDelServlet.del..."); } }
BookUpdServlet:
package com.Kissship.web; 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; /** * Servlet implementation class BookUpdServlet */ @WebServlet("/bookUpd.action") public class BookUpdServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("BookUpdServlet.upd..."); } }
BookAddServlet:
package com.Kissship.web; 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; /** * Servlet implementation class BookAddServlet */ @WebServlet("/bookAdd.action") public class BookAddServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("BookAddServlet.add..."); } }
效果图:
2.2版本二
index.jsp:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <p>版本1</p> 弊端:每一张表对应的每一个操作,都要写一个servlet类来处理<hr> <a href="bookAdd.action">增加</a> <a href="bookDel.action">删除</a> <a href="bookUpd.action">修改</a> <a href="bookList.action">查询</a> <hr><hr> <p>版本2</p> 弊端:每一张表对应的每一个操作,只要写一个servlet类来处理,但是每增加一个操作,都需要改变原有代码,换句话说要增加条件分支<hr> <a href="book.action?methodName=add">增加</a> <a href="book.action?methodName=del">删除</a> <a href="book.action?methodName=upd">修改</a> <a href="book.action?methodName=list">查询</a> </body> </html>
BookServlet:
package com.Kissship.web; 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; /** * Servlet implementation class BookAddServlet */ @WebServlet("/book.action") public class BookServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String methodName = request.getParameter("methodName"); if("add".equals(methodName)) { add(request,response); }else if("del".equals(methodName)){ del(request,response); }else if("upd".equals(methodName)){ upd(request,response); }else if("list".equals(methodName)){ list(request,response); } } private void list(HttpServletRequest request, HttpServletResponse response) { System.out.println("BookListServlet.List..."); } private void upd(HttpServletRequest request, HttpServletResponse response) { System.out.println("BookUpdServlet.upd..."); } private void del(HttpServletRequest request, HttpServletResponse response) { System.out.println("BookDelServlet.del..."); } private void add(HttpServletRequest request, HttpServletResponse response) { System.out.println("BookAddServlet.add..."); } }
效果图:
2.3版本三
index:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <p>版本1</p> 弊端:每一张表对应的每一个操作,都要写一个servlet类来处理<hr> <a href="bookAdd.action">增加</a> <a href="bookDel.action">删除</a> <a href="bookUpd.action">修改</a> <a href="bookList.action">查询</a> <hr><hr> <p>版本2</p> 弊端:每一张表对应的每一个操作,只要写一个servlet类来处理,但是每增加一个操作,都需要改变原有代码,换句话说要增加条件分支<hr> <a href="book.action?methodName=add">增加</a> <a href="book.action?methodName=del">删除</a> <a href="book.action?methodName=upd">修改</a> <a href="book.action?methodName=list">查询</a> <hr><hr> <p>版本3</p> 弊端:虽然解决了代码冗余的问题,但是放到项目范围,反射的代码是重复的<hr> <a href="book.action?methodName=add">增加</a> <a href="book.action?methodName=del">删除</a> <a href="book.action?methodName=upd">修改</a> <a href="book.action?methodName=list">查询</a> </body> </html>
BookServlet:
package com.Kissship.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; /** * Servlet implementation class BookAddServlet */ @WebServlet("/book.action") public class BookServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String methodName = request.getParameter("methodName"); // if("add".equals(methodName)) { // add(request,response); // }else if("del".equals(methodName)){ // del(request,response); // }else if("upd".equals(methodName)){ // upd(request,response); // }else if("list".equals(methodName)){ // list(request,response); // } try { Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class); m.setAccessible(true); m.invoke(this, request, response); } catch (Exception e) { e.printStackTrace(); } } private void list(HttpServletRequest request, HttpServletResponse response) { System.out.println("BookListServlet.List..."); } private void upd(HttpServletRequest request, HttpServletResponse response) { System.out.println("BookUpdServlet.upd..."); } private void del(HttpServletRequest request, HttpServletResponse response) { System.out.println("BookDelServlet.del..."); } private void add(HttpServletRequest request, HttpServletResponse response) { System.out.println("BookAddServlet.add..."); } }
效果图:
3.自定义MVC工作原理
4.MVC框架初步实现
4.1DispatherServlet
该类即对应工作原理图中的ActionServlet(中央控制器)。它需要拦截所有的action请求。
代码如下:
package com.Kissship.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 com.Kissship.web.BookAction; /** * 对应图中的ActionServlet:中央控制器 * @author jj * */ @WebServlet("*.action") public class DispatherServlet extends HttpServlet{ public Map<String, Action> actionmap = new HashMap<String, Action>();//通过Map集合去拿到Action类 @Override public void init() throws ServletException {//通过Map集合去拿到Action类之后,写一个初始化的方法init(),把所有的action放进去。(案例只有一个BookAction) actionmap.put("/book",new BookAction()); super.init(); } @Override protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { doPost(request, response); } @Override protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { // book -> BookAction -> add String uri = request.getRequestURI();//获取URL地址 uri = uri.substring(uri.lastIndexOf("/"),uri.lastIndexOf("."));//截取 Action action = actionmap.get(uri);//把所有的action放进init()方法之后,就可以通过map拿到uri,在这里返回一个action对象后续要通过它去抓取add等方法去提供服务 action.execute(request, response); } }
4.2Action类
子控制器:真正做事,处理浏览器请求的类。
代码如下:
package com.Kissship.framework; import java.io.IOException; import java.lang.reflect.Method; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; /** * 子控制器 * 真正做事,处理浏览器发送的请求的类 * @author jj * */ public class Action { protected void execute(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String methodName = request.getParameter("methodName"); try { Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class, HttpServletResponse.class); m.setAccessible(true); m.invoke(this, request, response); } catch (Exception e) { e.printStackTrace(); } } }
4.3BookAction
案例演示需要,Action(子控制器)的子类。
代码如下:
package com.Kissship.web; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.Kissship.framework.Action; public class BookAction extends Action{ public void add(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("BookAction.add..."); } public void del(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("BookAction.del..."); } public void upd(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("BookAction.upd..."); } public void list(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("BookAction.list..."); } }
4.4index.jsp
代码如下:
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%> <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <title>Insert title here</title> </head> <body> <p>版本1</p> 弊端:每一张表对应的每一个操作,都要写一个servlet类来处理<hr> <a href="bookAdd.action">增加</a> <a href="bookDel.action">删除</a> <a href="bookUpd.action">修改</a> <a href="bookList.action">查询</a> <hr><hr> <p>版本2</p> 弊端:每一张表对应的每一个操作,只要写一个servlet类来处理,但是每增加一个操作,都需要改变原有代码,换句话说要增加条件分支<hr> <a href="book.action?methodName=add">增加</a> <a href="book.action?methodName=del">删除</a> <a href="book.action?methodName=upd">修改</a> <a href="book.action?methodName=list">查询</a> <hr><hr> <p>版本3</p> 弊端:虽然解决了代码冗余的问题,但是放到项目范围,反射的代码是重复的<hr> <a href="book.action?methodName=add">增加</a> <a href="book.action?methodName=del">删除</a> <a href="book.action?methodName=upd">修改</a> <a href="book.action?methodName=list">查询</a> <hr><hr> <p>版本4</p> 解决了反射代码重复的问题<hr> <a href="book.action?methodName=add">增加</a> <a href="book.action?methodName=del">删除</a> <a href="book.action?methodName=upd">修改</a> <a href="book.action?methodName=list">查询</a> </body> </html>
效果图:
控制台:
BookAction为什么能调add方法?
因为在Action类中有了反射方法,然后BookAction继承了Action类。在BookAction类中都有一个反射的默认动态调用方法。所以BookAction里能直接add和删除修改等方法。
5.MVC框架初步实现个人理解思路
在中央控制器获取URL地址然后截取路径通过/book这个键去找一个对象(BookAction类——处理案例的浏览器请求),然后再BookAction上应该有个父类——即子控制器(真正做事,处理浏览器请求的类)。通过Map集合去拿到Action类之后, 然后写一个初始化的方法init(),把所有的action放进去。把所有的action放进init()方法之后,就可以通过map拿到uri。拿到了之后我们把反射方法copy到Action类中,然后在它的子类BookAction直接调用add和其他方法。最后在中央控制器调用反射代码块execute即可。
最后自定义MVC(上)就到这里,祝大家在敲代码的路上一路通畅!