一、引入案例
1 小案例
引出对 Tomcat 底层实现思考
1.1 完成小案例
1.1.1 运行效果
1.2 maven简要介绍
我们准备使用 Maven 来 创建一个 WEB 项目 , 先 简单给小伙伴介绍一下 Maven 是 什
么 , 更加详细的使用,我们还会细讲 , 现在先使用一把
1.3 创建maven的web项目
完成后,如果目录没有出现src,不要慌,自行百度搜索解决方案
1.4 配置阿里 maven 镜像
1.5 修改 pom.xml
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.hspedu</groupId><artifactId>tomcat02</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><name>tomcat02 Maven Webapp</name><!-- FIXME change it to the project's website --><url>http://www.example.com</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencies><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.11</version><scope>test</scope></dependency><!-- 引入servlet的依赖1. dependency 表示依赖, 也就是我们这个项目需要依赖的 jar 包2. groupId 和 artifactId 被统称为坐标, 是为了去定位这个项目/jar3. groupId: 一般是公司 比如 com.baidu , 这里是 avax.servlet4. artifactId 一般是项目名, 这里是 javax.servlet-api5. 这样的化就可以定位一个 jar 包6. version 表示你引入到我们项目的 jar 包的版本是 3.1.07. scope: 表示作用域,也就是你引入的 jar 包的作用范围8. provided 表示在 tomcat 本身是有这个 jar 的,因此在编译,测试使用,但是在打包发布就不用要带上9. 在默认情况下, 引入的 jar 会到 中央仓库去下载 https://mvnrepository.com/10. 会下载到哪里到你指定的目录 C:\Users\Administrator\.m2\repository11. 有时为了下载更快, 往往配置镜像,12. 在 默 认 的 路 径 下 拷 贝 一 份 setting.xml 到C:\Users\Administrator\.m2\settings.xml13. 指定默认的阿里云镜像--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency></dependencies><build><finalName>tomcat02</finalName><pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --><plugins><plugin><artifactId>maven-clean-plugin</artifactId><version>3.1.0</version></plugin><!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --><plugin><artifactId>maven-resources-plugin</artifactId><version>3.0.2</version></plugin><plugin><artifactId>maven-compiler-plugin</artifactId><version>3.8.0</version></plugin><plugin><artifactId>maven-surefire-plugin</artifactId><version>2.22.1</version></plugin><plugin><artifactId>maven-war-plugin</artifactId><version>3.2.2</version></plugin><plugin><artifactId>maven-install-plugin</artifactId><version>2.5.2</version></plugin><plugin><artifactId>maven-deploy-plugin</artifactId><version>2.8.2</version></plugin></plugins></pluginManagement></build>
</project>
1.7 cal.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>计算器</title>
</head>
<body>
<h1>计算器</h1>
<form action="/calServlet" method="get">num1:<input type="text" name="num1"><br/>num2:<input type="text" name="num2"><br/><input type="submit" value="提交">
</form>
</body>
</html>
1.8 创建 java 目录,存放 java 源文件
1.9 创建 CalServlet.java
package com.hspedu.servlet;import com.hspedu.utils.WebUtils;import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;public class CalServlet extends HttpServlet {protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {//接收提交的数据进行计算String strnum1 = request.getParameter("num1");String strnum2 = request.getParameter("num2");//把strnum1和strnum2转成intint num1= WebUtils.parseIt(strnum1,0);int num2=WebUtils.parseIt(strnum2,0);int result=num1+num2;response.setContentType("text/html;charset=utf-8");PrintWriter writer = response.getWriter();writer.print("<h1>"+num1+" + "+num2+" = "+result+"<h1>");writer.flush();writer.close();}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {doPost(request,response);}
}
1.10 修改 web.xml , 配置 Servlet
1.11 创 建 工 具类
package com.hspedu.utils;public class WebUtils {public static int parseIt (String strNum,int defaultVal){try {return Integer.parseInt(strNum);} catch (NumberFormatException e) {System.out.println(strNum+" 格式不对,转换失败");return defaultVal;}}
}
没有配置tomcat记得配置哦,
我们的目标 : 不用 Tomcat, 不用系统提供的 Servlet, 模拟 Tomcat 底层实现并能调用我
们自己设计的 Servle, 也能完成相同的功能
二、 Tomcat 整体架构分析
1 一图胜千言
- 说明: Tomcat 有三种运行模式(BIO, NIO, APR), 因为核心讲解的是 Tomcat 如何接收客户端请求,解析请求, 调用 Servlet , 并返回结果的机制流程, 采用 BIO 线程模型来模拟.[绘图]
三、实现
1 实现任务阶段 1
编写自己 Tomcat, 能给浏览器返回 Hi, Hspedu
1.1 基于 socket 开发服务端-流程
1.2 需求分析/图解
需求分析如图, 浏览器请求 http://localhost:8080/??, 服务端返回 hi , hspedu
1.3 分析+代码实现
1.3.1 分析示意图
1.3.2 代码实现
package com.hspedu.tomcat;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;/*
这是第一个版本的tomcat,可以完成接收浏览器的请求,并返回信息*/
public class LinranTomcatV1 {public static void main(String[] args) throws IOException {//1.创建ServerSocket serverSocket=new ServerSocket(8080);System.out.println("服务器正在8080端口监听");while (!serverSocket.isClosed()){//等待浏览器/客户端的链接//如果有连接,就创建一个socket//这个socket就是服务器和浏览器的连接通道Socket socket=serverSocket.accept();//先接收浏览器发送的数据//inputStream是字节流=》BufferedReader(字符流)InputStream inputStream = socket.getInputStream();BufferedReader bufferedReader=new BufferedReader(new InputStreamReader(inputStream,"utf-8"));//接收到浏览器发送的数据System.out.println("==============接收到浏览器发送的数据==============");String mes=null;//循环读取while ((mes=bufferedReader.readLine())!=null){if(mes.length()==0){break;//退出}System.out.println(mes);}//我们的tomcat回送数据OutputStream outputStream = socket.getOutputStream();//构建一个 http 响应的头//\r\n 表示换行//http 响应体,需要前面有两个换行 \r\n\r\nString respHeader = "HTTP/1.1 200 OK\r\n" +"Content-Type: text/html;charset=utf-8\r\n\r\n";String resp = respHeader + "hi, hspedu 韩顺平教育";System.out.println("========我们的 tomcat 给浏览器会送的数据 ======");System.out.println(resp);outputStream.write(resp.getBytes());//将 resp 字符串以 byte[] 方式返回//关闭outputStream.flush();outputStream.close();inputStream.close();socket.close();}}
}
问题分析:没有使用 BIO 线程模型,没有实现多线程,性能差
2 实现任务阶段 2
使用 BIO 线程模型,支持多线程
2.1 BIO 线程模型介绍
2.2 需求分析/图解
浏览器请求 http://localhost:8080, 服务端返回 hi , hspedu, 后台 hsptomcat 使用 BIO 线程模型 , 支持多线程 => 对前面的开发模式进行改造
2.3 分析+代码实现
2.3.1 分析示意图
2.3.2 代码实现
新建一个线程类
package com.hspedu.tomcat.handler;
/*HspRequestHandler对象是一个线程对象处理http请求的*/import java.io.*;
import java.net.Socket;public class HspRequestHandler implements Runnable {//定义一个socketprivate Socket socket=null;public HspRequestHandler(Socket socket) {this.socket = socket;}@Overridepublic void run() {//这里我们可以对客户端/浏览器进行通信try {InputStream inputStream = socket.getInputStream();//把inputStream->bufferedReaderBufferedReader bufferedReader =new BufferedReader(new InputStreamReader(inputStream, "utf-8"));String mes;System.out.println("=============HspRequestHandler接收的信息如下=================");while ((mes=bufferedReader.readLine())!=null){if(mes.length()==0){break;}System.out.println(mes);}//返回数据给我们的浏览器->封装成http响应OutputStream outputStream = socket.getOutputStream();//构建http响应String respHeader = "HTTP/1.1 200 OK\r\n" +"Content-Type: text/html;charset=utf-8\r\n\r\n";String resp = respHeader + "hi, hspedu 韩顺平教育";System.out.println("========我们的 tomcat 给浏览器会送的数据 ======");System.out.println(resp);outputStream.write(resp.getBytes());//将 resp 字符串以 byte[] 方式返回outputStream.flush();outputStream.close();inputStream.close();socket.close();} catch (IOException e) {e.printStackTrace();} finally {//最后一定确保socket要关闭if(socket!=null){try {socket.close();} catch (IOException e) {e.printStackTrace();}}}}
}
主程序
package com.hspedu.tomcat;import com.hspedu.tomcat.handler.HspRequestHandler;import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;public class LinranTomcatV2 {public static void main(String[] args) throws IOException {ServerSocket serverSocket=new ServerSocket(8080);System.out.println("============LinranTomcatV2在8080监听===============");while (!serverSocket.isClosed()){//只要serverSocket没有关闭,就一直等待连接Socket socket = serverSocket.accept();//这个socket就是一个通信//创建一个线程对象,并把socket给该线程new Thread( new HspRequestHandler(socket)).start();}}
}
3 实现任务阶段
处理 Servlet
3.1 Servlet 生命周期-回顾
3.2 需求分析/图解
3.3 分析
代码实现参考视频和项目源码
很心塞的一点,在跟着学习的过程中,最终 报错了,很烦