了解一下类似Spring MVC框架的底层实现
相信看完这个短文,MVC你就懂了,还不懂请艾特我······
带着问题去学习
- MVC到底是什么?
- 如何实现MVC?
开始!
MVC是什么?
Web开发
在讲MVC之前,还是简要介绍一下Web开发吧,相信搞程序的或多或少都知道Web编程、Socket编程等等。
其实开发Web应用最原始的方式面向Http连接的底层进行开发,自己编写Web服务,需要了解和考虑的内容包括但不限于:Socket编程、端口号、报文处理、IO多路复用、线程池等等。
举个例子,用Java来编写一个Http服务器:
public class Server { public static void main(String[] args) throws IOException { ServerSocket ss = new ServerSocket(8080); // 监听指定端口 System.out.println("server is running..."); for (;;) { Socket sock = ss.accept(); System.out.println("connected from " + sock.getRemoteSocketAddress()); Thread t = new Handler(sock); t.start(); } } } class Handler extends Thread { Socket sock; public Handler(Socket sock) { this.sock = sock; } public void run() { try (InputStream input = this.sock.getInputStream()) { try (OutputStream output = this.sock.getOutputStream()) { handle(input, output); } } catch (Exception e) { try { this.sock.close(); } catch (IOException ioe) { } System.out.println("client disconnected."); } } private void handle(InputStream input, OutputStream output) throws IOException { System.out.println("Process new http request..."); var reader = new BufferedReader(new InputStreamReader(input, StandardCharsets.UTF_8)); var writer = new BufferedWriter(new OutputStreamWriter(output, StandardCharsets.UTF_8)); // 读取HTTP请求: boolean requestOk = false; String first = reader.readLine(); if (first.startsWith("GET / HTTP/1.")) { requestOk = true; } for (;;) { String header = reader.readLine(); if (header.isEmpty()) { // 读取到空行时, HTTP Header读取完毕 break; } System.out.println(header); } System.out.println(requestOk ? "Response OK" : "Response Error"); if (!requestOk) { // 发送错误响应: writer.write("HTTP/1.0 404 Not Found\r\n"); writer.write("Content-Length: 0\r\n"); writer.write("\r\n"); writer.flush(); } else { // 发送成功响应: String data = "<html><body><h1>Hello, world!</h1></body></html>"; int length = data.getBytes(StandardCharsets.UTF_8).length; writer.write("HTTP/1.0 200 OK\r\n"); writer.write("Connection: close\r\n"); writer.write("Content-Type: text/html\r\n"); writer.write("Content-Length: " + length + "\r\n"); writer.write("\r\n"); // 空行标识Header和Body的分隔 writer.write(data); writer.flush(); } } }
一个Http服务器,本质上来说就是TCP服务器,处理流程就是服务器一直监听端口,客户端向指定端口发送请求,服务器接收请求进行相应的逻辑处理,而后将处理结果又返回给客户端,最后在客户端浏览器上进行展示。更详细的玩意儿还是参见《Unix网络编程》那本书的例子,可以模仿C语言写个Java的客户端。
但是在实际开发过程中,这些基础、底层的工作,就是重复造轮子,需要耗费大量的时间!
为了方便开发,将这些底层工作封装到一个库中——Servlet中,于是后来的程序员只需要基于Servlet的API来开发Web应用即可,不用再去管这些端口啊、IO多路复用什么的。
基于Servlet开发
基于Servlet开发可以大大减小开发Web服务的复杂程度,例如只需要几行代码,就可以让服务端相应客户端对主页的请求:
//原址https://www.liaoxuefeng.com/wiki/1252599548343744/1304265949708322 //WebServlet注解表示这是一个Servlet,并将url地址为"/"的请求映射到这个类中进行处理 @WebServlet(urlPatterns = "/") public class HelloServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 设置响应类型: resp.setContentType("text/html"); // 获取输出流: PrintWriter pw = resp.getWriter(); // 写入响应: pw.write("<h1>Hello, world!</h1>"); // flush强制输出: pw.flush(); } }
一个Web应用其实就是由一个或多个Servlet组成的,每个Servlet通过注解说明自己负责的路径Url,并通过实现doGet和doPost方法来进行相应的逻辑处理。比如处理GET方法,就覆写doGet()方法:
@WebServlet(urlPatterns = "/hello") public class HelloServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { ... } }
其他的如POST等请求方法也是类似的。
可以看到,当有了Servlet之后,开发Web应用就变得简洁了起来,只需要编写处理对应url的逻辑处理即可。
但是,你们有没有发现一个问题——在Java代码里出现了跟前端有关的代码。在Servlet程序中,输出HTML的方式如下:
PrintWriter pw = resp.getWriter(); pw.write("<html>"); pw.write("<body>"); pw.write("<h1>Welcome, " + name + "!</h1>"); pw.write("</body>"); pw.write("</html>"); pw.flush();
上述这种输出HTML的步骤较为繁琐,而且容易出错,并且也不像直接编写HTML网页那么直观(比如JS、CSS语言),虽然全栈工程师前后端都可以care,不过在项目开发中还是前端和后端的东西各自分开最好,才能使两者的开发效率最高。
为了解决这个问题,后面出现了JSP(Java Server Pages)语言。
JSP代码类似于html语言,并且其中还可以嵌入Java代码,进行变量插入、变量输出等操作:
//原址:https://www.liaoxuefeng.com/wiki/1252599548343744/1266262958498784 <html> <head> <title>Hello World - JSP</title> </head> <body> <%-- JSP Comment --%> <h1>Hello World!</h1> <p> <% out.println("Your IP address is "); %> <span style="color:red"> <%= request.getRemoteAddr() %> </span> </p> </body> </html>
不过这种方式(JSP)虽然可以很方便的输出HTML,并在其中实现一定的动态内容,运行简单的代码,但是写起Java来也忒不适应了吧,总感觉怪怪的。
另外,Servlet简化了Web应用的开发,可以基于Java语言实现复杂的业务逻辑,但是不太适合输出HTML。
那么,有没有一种方式可以结合两者,从而是两者相辅相成呢?
有的!那就是MVC!
MVC设计模式
先给出MVC设计模式的简单定义——Model-View-Controller:
- Model(模型)是前后端定义的数据传输单元,通常前端只是需要后端提供一些数据来展示而已,比如用户名、处理结果来,需要HTML输出的东西;
- View(视图)的工作就是接收后端发过来的Model,取出数据,然后写入Html中;
- Controller(控制器)其实就是业务逻辑,类似于之前的Servlet开发,区别在于现在将处理结果都放在Model里,然后发给View;
来个例子吧,实现用户登录,获取信息
首先,我们编写了两个JavaBean(JavaBean简单来说就是一个可以放入数据和取出数据的类),也就是之前说的Model:
//源码:https://www.liaoxuefeng.com/wiki/1252599548343744/1266264917931808 public class User { public long id; public String name; public School school; } public class School { public String name; public String address; }
接着,来实现Controller,在UserServlet中实现从数据库读取User、School等信息,并放入Request中,再发送给user.jsp,也就是View进行渲染处理:
@WebServlet(urlPatterns = "/user") public class UserServlet extends HttpServlet { protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { // 假装从数据库读取: School school = new School("No.1 Middle School", "101 South Street"); User user = new User(123, "Bob", school); // 放入Request中: req.setAttribute("user", user); // forward给user.jsp: req.getRequestDispatcher("/WEB-INF/user.jsp").forward(req, resp); } }
View——user.jsp负责将接收到的user信息进行渲染展示,即写入Html中:
<%@ page import="com.itranswarp.learnjava.bean.*"%> <% User user = (User) request.getAttribute("user"); %> <html> <head> <title>Hello World - JSP</title> </head> <body> <h1>Hello <%= user.name %>!</h1> <p>School Name: <span style="color:red"> <%= user.school.name %> </span> </p> <p>School Address: <span style="color:red"> <%= user.school.address %> </span> </p> </body> </html>
以上,就完成了一个简单的web应用,在浏览器访问http://localhost:8080/user,结果如下:
捋一捋,用一个结构图来描述上述代码:
简单来说,就是将整个Web开发分成了三个部分:逻辑处理、前端展示、数据传输,分别代表Controller、View、Model。
这种方式有什么好处?分工明确、开发简单。
Controller就好好关注逻辑处理,比如访问数据库啦、进行复杂的逻辑交互等等;View就关注如何让界面好看就完了,比如用模板引擎写出好看的界面,留几个空白给Model进行展示就ok了!
有关模板引擎的具体内容,可以了解:https://blog.csdn.net/qq_42266891/article/details/108265478
实现一个简单MVC框架
Java Web ——MVC基础框架讲解及代码演示(下):https://developer.aliyun.com/article/1508612