你只会说MVC模型是什么但是不会实现?今天带你走通Web、Servlet、MVC、SpringMVC。代码演示很清晰
创始人
2024-05-28 13:05:23
0

文章目录

  • HTTP请求和HTTP响应
  • 从0手写一个Web服务器,看看能有多累人
  • 使用Servlet实现一个服务器,看看多简单
    • Serlvet的创建
    • Servlet的运行
    • Servlet的其他问题
  • Servlet这么爽,我们简单地探索一下它的原理
  • JSP跟Servlet合作啦,我们来看一下他们能干点啥?
  • MVC框架其实你已经学完了
  • SpringMVC
    • 从应用入手,我们看看SpringMVC是怎样执行的
    • 从原理入手,我们看看SpringMVC是怎样执行的
  • SpringMVC常用注解

参考文章:廖雪峰入门Servlet
参考文章:MVC
参考文章:SpringMVC

HTTP请求和HTTP响应

HTTP请求,这是通过Socket.InputStream()进来的信息

GET / HTTP/1.1
Host: www.sina.com.cn
User-Agent: Mozilla/5.0 xxx
Accept: */*
Accept-Language: zh-CN,zh;q=0.9,en-US;q=0.8

HTTP响应,这是通过Socket.OutputStream()出去的信息

HTTP/1.1 200 OK
Content-Type: text/html
Content-Length: 21932
Content-Encoding: gzip
Cache-Control: max-age=300...网页数据...

从0手写一个Web服务器,看看能有多累人

这么多代码也太棒了吧!童鞋,这只是打印了一个HelloWorld,这么多代码会不会过分复杂了?

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 = "

Hello, world!

";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();}} }

在这里插入图片描述

使用Servlet实现一个服务器,看看多简单

Serlvet的创建

这就写完了?对的,我已经通过pw.writer将

Hello, world!

输出了,剩下的HTTP响应部分让Serlvet帮我补齐就行了。

@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("

Hello, world!

");// 最后不要忘记flush强制输出:pw.flush();} }

因此,在JavaEE平台上,处理TCP连接,解析HTTP协议这些底层工作统统扔给现成的Web服务器去做,我们只需要把自己的应用程序跑在Web服务器上。为了实现这一目的,JavaEE提供了Servlet API,我们使用Servlet API编写自己的Servlet来处理HTTP请求,Web服务器实现Servlet API接口,实现底层功能
在这里插入图片描述

当然,你要别人帮你干活,那肯定要指定一下谁来帮你干活,所以还要简单的配置一下Serlvet

pom.xml文件

4.0.0com.itranswarp.learnjava		web-servlet-hello		war			1.0-SNAPSHOTUTF-8UTF-8171717jakarta.servletjakarta.servlet-api5.0.0provided		hello	

Servlet的运行

省流:工程打包后,放入tomcat的webapps,启动tomcat,就可以访问了

  1. 运行Maven命令mvn clean package,在target目录下得到一个hello.war文件,这个文件就是我们编译打包后的Web应用程序。

  2. 普通的Java程序是通过启动JVM,然后执行main()方法开始运行。但是Web应用程序有所不同,我们无法直接运行war文件,必须先启动Web服务器,再由Web服务器加载我们编写的HelloServlet,这样就可以让HelloServlet处理浏览器发送的请求。

  3. 无论使用哪个服务器,只要它支持Servlet API 5.0(因为我们引入的Servlet版本是5.0),我们的war包都可以在上面运行。这里我们选择使用最广泛的开源免费的Tomcat服务器。

  4. 把hello.war复制到Tomcat的webapps目录下,然后切换到bin目录,执行startup.sh或startup.bat启动Tomcat服务器:

$ ./startup.sh
Using CATALINA_BASE: …/apache-tomcat-10.1.x
Using CATALINA_HOME: …/apache-tomcat-10.1.x
Using CATALINA_TMPDIR: …/apache-tomcat-10.1.x/temp
Using JRE_HOME: …/jdk-17.jdk/Contents/Home
Using CLASSPATH: …/apache-tomcat-10.1.x/bin/bootstrap.jar:…
Tomcat started.

  1. 在浏览器输入http://localhost:8080/hello/即可看到HelloServlet的输出:
    在这里插入图片描述

Servlet的其他问题

  1. Servlet版本问题
  • 要务必注意servlet-api的版本。4.0及之前的servlet-api由Oracle官方维护,引入的依赖项是javax.servlet:javax.servlet-api,编写代码时引入的包名为:import javax.servlet.*;
  • 而5.0及以后的servlet-api由Eclipse开源社区维护,引入的依赖项是jakarta.servlet:jakarta.servlet-api,编写代码时引入的包名为:import jakarta.servlet.*;
  • 对于很多仅支持Servlet 4.0版本的框架来说,例如Spring 5,我们就只能使用javax.servlet:4.0.0版本,这一点针对不同项目要特别注意。
  1. Servlet路径问题

上述项目中,为啥路径是/hello/而不是/?

  • 因为一个Web服务器允许同时运行多个Web App,而我们的Web App叫hello,因此,第一级目录/hello表示Web App的名字,后面的/才是我们在HelloServlet中映射的路径。
  • 省流:第一个\是给Tomcat中全部项目的,第二个\是才是给hello项目的

Servlet这么爽,我们简单地探索一下它的原理

整体的一个交互就是zei个样子的啦

Web Server:让Servlet运行起来的Web服务器,例如Tomcat。
Servlet Container:Servlet容器就是用来装Servlet的。
Servlet:以init、service、destroy为生命周期
在这里插入图片描述

在走进来看看Servlet Container中的Serlvet,一个工程有多个Class,每个Class就是一个Serlvet。像这里的就有三个Serlvet啦。

@WebServlet(urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {...
}@WebServlet(urlPatterns = "/signin")
public class SignInServlet extends HttpServlet {...
}@WebServlet(urlPatterns = "/")
public class IndexServlet extends HttpServlet {...
}

并且通过Dispatcher来选择路径指定的Servlet
在这里插入图片描述
我们来看看一个Servlet里面会有什么,拿HelloServlet看看,只列出了常用方法

@WebServlet(urlPatterns = "/hello")
public class HelloServlet extends HttpServlet {//在 Servlet 的生命期中,仅执行一次 init() 方法。它是在服务器装入 Servlet 时执行的。init();//缺省服务: 如果 HTTP 请求方法为 GET,则缺省情况下就调用 doGet() ,否则调用doPost()service(HttpServletRequest req, HttpServletResponse resp);//执行动作,doGet/doPost二选一doGet(HttpServletRequest req, HttpServletResponse resp);//执行动作,doGet/doPost二选一doPost(HttpServletRequest req, HttpServletResponse resp);//destroy() 方法仅执行一次,即在服务器停止且卸装Servlet 时执行该方法。destroy()}
}

什么,你还想看doGet/doPost做了什么动作?自己去上面看实例吧

JSP跟Servlet合作啦,我们来看一下他们能干点啥?

省流:JSP就是Servlet,通过Servlet + JSP分离前后端代码,实际上是将一个Servlet任务分给了两个Servlet去完成。
好了好了,上面我们小打小闹完成了HelloWord,现在我们要大打大脑,把整个html网页输出。就像把www.hao123.com那个网页输出那样。我们来看看代码。

@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("");pw.write("");pw.write("

Welcome, " + name + "!

");pw.write("");pw.write("");// 最后不要忘记flush强制输出:pw.flush();} }

其实能看我这篇文章的童鞋也大概知道前端后端的区别了,上述这段代码明显就是前端跟后端整合在一起了,这样好吗?这样不好。那就把它给分开咯。

下面换一个实例来演示如何使用JSP+Servlet将前后端分离。

省流:下面这个实例就是通过一个forward()函数,从Servlet跳转到JSP,完成前后端分离
首先编写两个JavaBean

public class User {public long id;public String name;public School school;
}public class School {public String name;public String address;
}

UserServlet中,我们可以从数据库读取UserSchool等信息,然后,把读取到的JavaBean先放到HttpServletRequest中,再通过forward()传给user.jsp处理

@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);}
}

user.jsp中,我们只负责展示相关JavaBean的信息,不需要编写访问数据库等复杂逻辑:

<%@ page import="com.itranswarp.learnjava.bean.*"%>
<%User user = (User) request.getAttribute("user");
%>

Hello World - JSP

Hello <%= user.name %>!

School Name:<%= user.school.name %>

School Address:<%= user.school.address %>

我们在浏览器访问http://localhost:8080/user,请求首先由UserServlet处理,然后交给user.jsp渲染
在这里插入图片描述

从上面这个例子不难看出项目整个运行流程

  • 需要展示的User被放入HttpServletRequest中以便传递给JSP,因为一个请求对应一个HttpServletRequest,我们也无需清理它,处理完该请求后HttpServletRequest实例将被丢弃;
  • user.jsp放到/WEB-INF/目录下,是因为WEB-INF是一个特殊目录,Web Server会阻止浏览器对WEB-INF目录下任何资源的访问,这样就防止用户通过/user.jsp路径直接访问到JSP页面;
  • JSP页面首先从request变量获取User实例,然后在页面中直接输出,此处未考虑HTML的转义问题,有潜在安全风险。

这样,我们就把从数据库获取数据的业务逻辑留给了Serlvet后台,把输出HTML网页的任务交给了JSP前端。

思考一下,为什么我们能够将一个前后端Servlet分成后端Servlet+前端JSP,其实给人的感觉就是Java跟C语言一起跑一个项目,有点不可思议。其实不是这样的,因为JSP就是Servlet。

我们现在将JSP转化成Servlet,其实就是这样:


Hello World - JSP

<%-- JSP Comment --%>

Hello World!

<%out.println("Your IP address is ");%><%= request.getRemoteAddr() %>

JSP对应Servlet

package org.apache.jsp;
import ...public final class hello_jsp extends org.apache.jasper.runtime.HttpJspBaseimplements org.apache.jasper.runtime.JspSourceDependent,org.apache.jasper.runtime.JspSourceImports {...public void _jspService(final javax.servlet.http.HttpServletRequest request, final javax.servlet.http.HttpServletResponse response)throws java.io.IOException, javax.servlet.ServletException {...out.write("\n");out.write("\n");out.write("    Hello World - JSP\n");out.write("\n");out.write("\n");...}...
}

是不是看傻了?我再给你看一个东西,我把JSP文件放在tomcat\webapps下

<%@ page contentType="text/html; charset=gb2312" language="java" %>  
  
  
  
第一个JSP页面  
    <%for(int i=0;i<10;i++)  {  out.println(i);  %>  
<%}%>

在这里插入图片描述
在这里插入图片描述
所以我们最后的一个总结就是

JSP就是Servlet,通过Servlet + JSP分离前后端代码,实际上是将一个Servlet任务分给了两个Servlet去完成。

MVC框架其实你已经学完了

在这里插入图片描述
Of course,I am 确定,童鞋们真的已经学完了MVC,不信你们看一张图。
在这里插入图片描述
按照鄙人浅薄的项目经历(大二)来说,你以后做的项目确实就是差不过是这样子的流程了,只不过在业务处理那块会复杂一些,把后端业务处理分成controller、Service、Mapper三层架构。

接下来就要开始SpringMVC学习啦!太快了受不了?那你自己去一旁休息一下吧,哥哥是老司机,要带其他童鞋上高速了,dududu~

SpringMVC

前言
我曾经遇到一篇很牛逼的文章,上来直接跟我说SpringMVC组件,看的我两眼发光。我使劲背,又使劲忘,背忘背忘背忘背忘背忘背忘…,最后都忘了背了。所以如果你不看我前面对Servlet精彩的讲解,那你就先不要看这里,因为要讲SpringMVC必须基于ServletMVC

屏幕前的读者可能有一部分已经做过SpringBoot项目了,你们可能会根据自己SpringBoot项目经历结合网上的SpringMVC组件讲解去理解SpringMVC,我个人认为不要这么做,因为SpringBoot你知道的,简化了很多东西。最好从一个原始的SpringMVC项目开始,下面就给你们带来一个完整的SpringMVC原始实例。

从应用入手,我们看看SpringMVC是怎样执行的

  1. 导包包,导个jar包!

junitjunit4.11test


org.slf4jslf4j-log4j121.7.21


javax.servletjavax.servlet-api3.1.0

javax.servlet.jspjsp-api2.2

javax.servletjstl1.2


mysqlmysql-connector-java5.1.35


org.springframeworkspring-web5.2.3.RELEASE

org.springframeworkspring-webmvc5.2.3.RELEASE

org.springframeworkspring-context5.2.3.RELEASE

org.springframeworkspring-test5.2.3.RELEASE

org.springframeworkspring-jdbc5.2.3.RELEASE

com.github.stefanbirknersystem-rules1.16.1test

org.aspectjaspectjweaver1.8.9


org.apache.commonscommons-lang33.4

commons-fileuploadcommons-fileupload1.3.1

  1. Spring MVC 应用时需要在 web.xml 中部署 DispatcherServlet。

其中我们需要重点注意的是:DispatcherServlet会怎样寻找我们定义的Servlet的配置文件。如果我们使用了标签指定了路径,那就使用该路径来查找我们的Servlet配置文件,如果没有就按照默认路径应用程序的 WEB-INF 目录下查找我们的Servlet配置文件。

Servlet 是 DispatcherServlet 类型,它就是 Spring MVC 的入口,并通过 1 配置标记容器在启动时就加载此 DispatcherServlet,即自动启动。然后通过 servlet-mapping 映射到“/”,即 DispatcherServlet 需要截获并处理该项目的所有 URL 请求。


springMVCspringmvcorg.springframework.web.servlet.DispatcherServletcontextConfigLocationclasspath:springmvc-servlet.xml1springmvc/

  1. 为我们自定义的Servlet创建配置文件



  1. 创建Controller
package controller;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;public class LoginController implements Controller {public ModelAndView handleRequest(HttpServletRequest arg0,HttpServletResponse arg1) throws Exception {return new ModelAndView("/WEB-INF/jsp/register.jsp");}
}
package controller;import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;public class RegisterController implements Controller {public ModelAndView handleRequest(HttpServletRequest arg0,HttpServletResponse arg1) throws Exception {return new ModelAndView("/WEB-INF/jsp/login.jsp");}
}
  1. 创建View

index.jsp:这个文件不要不要不要放在WEB-INF中,还记得我说过不能直接访问WEB-INF里面的内容吗?

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>




Insert title here

未注册的用户,请 注册
已注册的用户,去 登录

WEB-INF 下创建 jsp 文件夹,将 login.jsp 和 register.jsp 放到 jsp 文件夹下

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>




Insert title here

登录页面!


register.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8" %>


Insert title here
注册页面!



将 springmvcDemo 项目部署到 Tomcat 服务器。首先访问 index.jsp 页面。

在下图所示的页面中,当用户单击“注册”超链接时,根据 springmvc-servlet.xml 文件中的映射将请求转发给 RegisterController 控制器处理,处理后跳转到 /WEB-INF/jsp 下的 register.jsp 视图。同理,当单击“登录”超链接时,控制器处理后转到 /WEB-INF/jsp下的 login.jsp 视图。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

那么我们来看一下总的流程是什么,顺道用到了SpringMVC的哪些组件

  • 通过web.xml注册DispatcherServlet,并向HandlerMapping发送请求
  • HandlerMapping根据请求返回Controller执行链
  • 通过HandlerAdapter完成Controller的对接。因为SpringMVC中的Handler可以是任意形式,只要能处理请求就可以,但是Servlet需要的处理方法的结构却是固定的,都是以request和response为参数的方法。如何让固定的Servlet处理方法调用灵活的Handler来进行处理呢?这HandlerAdapter要做的事情。
  • 接下来就是执行controller里面的方法,并且返回一个ModelAndView对象
  • ModelAndView就是指定的.jsp,我们需要解析然后返回给view
  • 最后DispatcherServletview返回给浏览器

在这里插入图片描述

从原理入手,我们看看SpringMVC是怎样执行的

其实你又已经学完了SpringMVC原理了,因为上面已经从实例出发,一步步跟你讲解了SpringMVC的运行原理,我相信如果我在这里单独讲原理你是绝对不会看第二遍,甚至第一遍就粗略看一下而已。所以恭喜你,整个Web服务,你已经入门了。

SpringMVC常用注解

参考文章
这里根据自己需求记录笔记,我这里就只记录自己不熟悉的注解好了

@RequestMapping

RequestMapping注解有六个属性

  • value:指定请求的实际地址
  • method: 指定请求的method类型, GET、POST、PUT、DELETE等
  • consumes:指定处理请求的提交内容类型
  • produces:指定返回的内容类型
  • params: 指定request中必须包含某些参数值是,才让该方法处理
  • headers:指定request中必须包含某些指定的header值,才能让该方法处理

@PathVariable

 @RequestMapping(value="/user/{userId}/roles/{roleId}",method = RequestMethod.GET)  public String getLogin(@PathVariable("userId") String userId,  @PathVariable("roleId") String roleId){}

@RequestParam

 @RequestMapping(value="/dept/get/{id}",method = RequestMethod.GET)  public String getLogin(@RequestParam(value = "id") int id,  @PathVariable("roleId") String roleId){}

相关内容

热门资讯

商会会长中秋致辞 商会会长中秋致辞尊敬的各位领导、各位来宾、女士们、先生们:大家好! 金风送爽、春华秋实,又是一年一度...
学校庆祝教师节座谈会主持词 2021年学校庆祝教师节座谈会主持词  根据活动对象的不同,需要设置不同的主持词。在当今中国社会,主...
主持的谢幕词 有关主持的谢幕词(通用10篇)  导语:谢幕是指演出结束后观众鼓掌时,演员站在台上向观众弯腰敬礼答谢...
一年级新队员入队仪式主持词 一年级新队员入队仪式主持词  主持词是各种演出活动和集会中主持人串联节目的串联词。在人们越来越多的参...
《老表,你好Hea》经典台词 《老表,你好Hea》经典台词  1.我是林在野,我的人生只有一个目标,就是抗议。——林在野  2.我...
艺术节闭幕式闭幕词 艺术节闭幕式闭幕词(通用6篇)  契合现场环境的闭幕词能给集会带来双倍的效果。在如今这个中国,闭幕词...
元旦茶话会主持词 元旦茶话会主持词6篇  一年的新年的钟声即将敲响,时光的车轮又留下了一道深深的印痕。有很多学校班级或...
入队仪式主持词 入队仪式主持词  一、什么是主持词  由主持人于节目进行过程中串联节目的串联词。如今的各种演出活动和...
幼儿园元旦文艺汇演节目串词 幼儿园元旦文艺汇演节目串词幼儿园2011年元旦文艺汇演节目串词a:尊敬的各位领导、各位家长b:亲爱的...
婚礼宴会致辞 婚礼宴会致辞(集锦15篇)  在平日的学习、工作和生活里,要用到致辞的地方还是很多的,致辞具有有张有...
座谈会主持词 座谈会主持词  主持词的内容  主持词:一般由开场白、中间部分与结束语组成。  开场白:演出或其他开...
论坛原创作品朗诵晚会主持词 论坛原创作品朗诵晚会主持词电力网”溪水论坛”原创作品朗诵晚会晚会监督:一秀.北京诗鸽晚会男主持:骑士...
郭德纲相声梦中婚台词 郭德纲相声梦中婚台词  导语:相声用笑话、滑稽地问答、说唱等引起观众发笑的一种曲艺形式。用笑话、滑稽...
父亲生日祝寿词 父亲生日祝寿词父亲生日祝寿词各位亲友、各位来宾:晚上好!首先我代表我们*氏家族向各位的光临表示热烈的...
歌咏比赛主持词 歌咏比赛主持词(精选10篇)  根据活动对象的不同,需要设置不同的主持词。在当今中国社会,主持成为很...
全体教师会主持词 全体教师会主持词  主持词的写作需要将主题贯穿于所有节目之中。在当今社会生活中,很多场合都需要主持人...
清明诗会主持词 清明诗会主持词  主持词是主持人在台上表演的灵魂之所在。时代不断在进步,主持人参与的事情越来越多,主...
中秋晚会董事长精彩致辞 中秋晚会董事长精彩致辞(精选5篇)  在生活、工作和学习中,大家都用到过致辞吧,致辞具有能伸能缩,可...
证婚人婚礼致辞 证婚人婚礼致辞(15篇)  在现实生活或工作学习中,许多人都有过写致辞的经历,对致辞都不陌生吧,在各...
专卖店开业主持词 专卖店开业主持词  主持词要注意活动对象,针对活动对象写相应的主持词。现今社会在不断向前发展,主持词...