文章目录
前言
手动实现 Tomcat 底层机制+ 自己设计 Servlet。模拟Tomcat底层实现并能调用我们自己设计的Servlet实现一个简单的小案例。
一步步循序渐进,图解+分析+代码分三个阶段(三个版本 循序渐进)来实现.
所运用到的知识:
先看看最终版效果图
输入有效的地址访问正确
输入无效的地址返回404 not Found
一、Tomcat整体架构分析
不用 Tomcat, 不用系统提供的 Servlet, 模拟 Tomcat 底层实现并能调用我
们自己设计的 Servle, 也能完成相同的功能
总体思路流程图:
二、实现Tomcat底层机制+设计Servlet
1、阶段1——拿到客户端/浏览器发送的请求,回送”Hello World!”
1.1 基于socket开发服务端–流程
1.2 需求分析/图解
分析示意图:
1.3 分析+代码实现
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>计算器</title>
</head>>
<body>
<h1>计算器</h1>
<form action="/lhTomcat/calServlet" method="get">
num1:<input type="text" name="num1"><br/>
num2:<input type="text" name="num2"><br/>
<input type="submit" value="提交">
</form>
</body>
</html>
package xyz.lhweb.lhTomcat;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* lh tomcat
*第一个版本的tomcat 可以完成接受浏览器发送的请求,并返回相关信息
* @author 罗汉 QQ;1072344372
* @date 2023/03/01
*/
public class LhTomcatV1 {
public static void main(String[] args) throws IOException {
//1 创建ServletSocket 在8080端口监听
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("==========================lhTomcat在8080端口监听======================");
// 如果没有关闭就一直在等待监听
while (!serverSocket.isClosed()){
/**
* 等待浏览器/客户端的连接
* 如果有连接来,就创建一个socket
* 这个socket就是服务端和浏览器/客户端之间的通道
*/
Socket socket = serverSocket.accept();
/**
* 先接收浏览器发送的信息
* inputStream是字节流 为了读取方便转成一个BuffreadReader字符流。可以整行整行的读取
*/
InputStream inputStream = socket.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
String msg=null;
System.out.println("==================接收到浏览器发送的数据=================");
//循环的读取
while ((msg=bufferedReader.readLine())!=null){
//判断mes的长度是否为0
if(msg.length()==0){
break;
}
System.out.println(msg);
}
//我们的tomcat回送http响应方式
OutputStream outputStream = socket.getOutputStream();
/**
* 因为要按照http响应的方式,所以要构建一个http响应的消息头
* rn代表换行
* 响应体和响应行之间有一个空行 所以是rnrn 相当于换行后空一行
*/
String respHeader="HTTP/1.1 200rn" +
"Content-Type: text/html;charset=utf-8rnrn";
String resp=respHeader+"Hello World!";
//将resp字符串以byte[]方式返回
outputStream.write(resp.getBytes());
//关闭流
outputStream.flush();
outputStream.close();
inputStream.close();
socket.close();
}
}
}
1.4 测试 浏览器:http://localhost:8080/
1.5 问题分析
没有使用BIO线程模型,没有实现多线程,性能差
目标:拿到客户端/浏览器发送的请求,回送”Hello World!”
2、阶段2——使用BIO线程模型实现多线程
2.1 BIO线程模型介绍
2.2 需求分析/图解
需求分析: 浏览器请求 http://localhost:8080, 服务端返回 Hello World!后台lhtomcat 使用 BIO 线程模型,支持多线程=> 对前面的开发模式进行改造
图解:
2.3 分析+代码实现
分析:原来是在lhtomcat对数据的返回和处理,现在改到在线程中对数据的返回和处理。现在的lhtomcat相当于一个前台的身份,不具体的负责业务处理。只负责把Socket通道传给线程。
项目结构:
LhRequestHandler.java
package xyz.lhweb.lhTomcat.handler;
import java.io.*;
import java.net.Socket;
/**
* lh请求处理程序
*
* @author 罗汉 QQ;1072344372
* @date 2023/03/09
*/
public class LhRequestHandler implements Runnable{
//定义socket
private Socket socket=null;
public LhRequestHandler(Socket socket) {
this.socket = socket;
}
@Override
public void run(
) {
// 这里我们可以对客户端/浏览器进行IO编程/交互
try {
InputStream inputStream = socket.getInputStream();
//把inputStream转成BufferedReader 方便按行读取
BufferedReader bufferedReader =
new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
System.out.println("当前线程:-------------"+Thread.currentThread().getName());
System.out.println("==================lhtomcat2 接收到的数据如下====================");
String msg=null;
//如果我读取到的信息不为空,说明可以继续读取
while ((msg=bufferedReader.readLine())!=null){
//如果长度为0 "" 退出
if(msg.length()==0){
break;
}
System.out.println(msg);
}
//构建一个响应头
//返回的http的响应体和响应头之间有2个换行rnrn
String respHeader="HTTP/1.1 200rn" +
"Content-Type: text/html;charset=utf-8rnrn";
String resp=respHeader+"<h1>hi lhTomcatV2</h1>";
System.out.println("==================lhtomcat2 返回的数据如下====================");
System.out.println(resp);
//从socket返回数据给我们的浏览器/客户端 封装成http响应格式返回
OutputStream outputStream = socket.getOutputStream();
//resp.getBytes() 把字符串转成字节数组
outputStream.write(resp.getBytes());
//关闭流
outputStream.flush();
outputStream.close();
inputStream.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}finally {
//最后一定确保socket要关闭
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
LhTomcatV2.java
package xyz.lhweb.lhTomcat;
import xyz.lhweb.lhTomcat.handler.LhRequestHandler;
import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;
/**
* lh tomcat
*第一个版本的tomcat 可以完成接受浏览器发送的请求,并返回相关信息
* @author 罗汉 QQ;1072344372
* @date 2023/03/09
*/
public class LhTomcatV2 {
public static void main(String[] args) throws IOException {
//1 创建ServletSocket 在8080端口监听
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("==========================lhTomcatV2在8080端口监听======================");
// 如果没有关闭就一直在等待监听
while (!serverSocket.isClosed()){
/**
* 1 等待浏览器/客户端的连接如果有连接来,就创建一个socket
* 2 这个socket就是服务端和浏览器/客户端之间的通道
*/
Socket socket = serverSocket.accept();
//3 创建一个线程对象,并且吧socket传给线程
new Thread(new LhRequestHandler(socket)).start();
}
}
}
2.4 测试 浏览器: http://localhost:8080/
2.5 问题分析:
lhTomcatV2只是简单返回结果,没有和Servlet、web.xml关联。每次请求都会开启一个线程,不是多路复用的。BIO是有一点问题的,没有NIO(多路复用)好用。将在第三阶段引入servlet
3、阶段3——处理Servlet及使用容器(特别重要)
3.1 Servlet生命周期-回顾
3.2 需求分析/图解
图解:
先搭建结构,再编写内容
定义接口,抽象类,实现类(注:实现类为LhServletV3。不能和接口同名)
类图如下
3.3 分析+代码实现
浏览器给tomcat发起请求后,tomcat会把请求封装成HttpServletRequest对象,返回数据是HttpServletResponse对象
原生的sevlet里有这2个接口,我们需要自己定义这2个对象来作代替
3.3.1封装Request
LhRequest.java编写思路
首先在构建LhRequest对象的时候 构造器 =>对http请求进行封装
获取到请求的第一行然后分割提取
请求方式method
地址url
参数列表parametersMap
代码展示
package xyz.lhweb.lhTomcat.http;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
/**
* lh请求
* 1 LhRequest作用是封装http请求的数据
* 2 比如 method get/post uri,参数列表
* 例如:http://localhost:8080/lhServletV3?name=1&password=2 封装的就是 /lhServletV3?name=1&password=2
* 3 LhRequest等价原生servlet 中的HttpServletRequest
* @author 罗汉 QQ;1072344372
* @date 2023/03/09
* 4 现在只考虑get请求
*/
public class LhRequest {
private String method;
private String uri;
private InputStream inputStream=null;
//参数列表 key value 所以用hashMap
private final HashMap<String,String>parametersMap=
new HashMap<>();
//构造器 =>对http请求进行封装
//inputStream是和对应的 http请求的socket关联
public LhRequest(InputStream inputStream){
this.inputStream=inputStream;
//完成对http请求数据的封装
init();
}
private void init(){
try {
//为了读取方便 inputStream-> BufferedReader InputStreamReader: 转换流
BufferedReader bufferedReader =
new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8));
//读取第一行
/*原始请求 http://localhost:8080/lhServletV3?name=1&password=2
* GET /lhServletV3?name=1&password=2 HTTP/1.1
Host: localhost:8080
* */
String requestLine = bufferedReader.readLine();//GET /lhServletV3?name=1&password=2 HTTP/1.1
//按照空格分成一个数组
String[] requestLineArr=null;
if(requestLine!=null){
requestLineArr = requestLine.split(" ");
}
//得到method
if(requestLineArr!=null){
method=requestLineArr[0];
}
//得到uri:/lhServletV3 可以用正则表达式
//看看是否有参数列表
assert requestLineArr != null;
int index=requestLineArr[1].indexOf("?");
if (index==-1){//说明没有参数列表
uri=requestLineArr[1];
}else {//有参数列表
//[0,index)
uri=requestLineArr[1].substring(0,index);//取了/lhServletV3
//获取参数列表 放到parametersMap中去
//parameters 相当于 name=1&password=2
String parameters = requestLineArr[1].substring( index + 1);
String[] parametersPair = parameters.split("&");
//防止用户提交时http://localhost:8080/lhServletV3? 后面只给?不给参数
// if(null!=parametersPair&& "".equals(parametersPair)){
//再次分割 parameterPair= name=1
for (String parameterPair : parametersPair) {
//parameterVal["name","1"]
String[] parameterVal = parameterPair.split("=");
if (parameterVal.length==2){//说明的的确确有参数值
//放入到parametersMap里去
parametersMap.put(parameterVal[0],parameterVal[1]);
}
// }
}
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
// request对象有一个特别重要的方法
public String getParameter(String name){
if(parametersMap.containsKey(name)){
return parametersMap.get(name);
}else {
return "";
}
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getUri() {
return uri;
}
public void setUri(String uri) {
this.uri = uri;
}
@Override
public String toString() {
return "LhRequest{" +
"method='" + method + ''' +
", uri='" + uri + ''' +
", parametersMap=" + parametersMap +
'}';
}
}
LhRequestHandlerV3_TestLhRequest.java
package xyz.lhweb.lhTomcat.handler;
import xyz.lhweb.lhTomcat.http.LhRequest;
import java.io.*;
import java.net.Socket;
/**
* 对LhRequest单独进行测试
*
* @author 罗汉 QQ;1072344372
* @date 2023/03/09
*/
public class LhRequestHandlerV3_TestLhRequest implements Runnable{
//定义socket
private Socket socket=null;
public LhRequestHandlerV3_TestLhRequest(Socket socket) {
this.socket = socket;
}
@Override
public void run(
) {
// 这里我们可以对客户端/浏览器进行IO编程/交互
try {
InputStream inputStream = socket.getInputStream();
//对LhRequest进行测试
LhRequest lhRequest = new LhRequest(inputStream);
String name = lhRequest.getParameter("name");
String password = lhRequest.getParameter("password");
System.out.println("LhRequest测试:--------------------------name="+name+"---------------------------");
System.out.println("LhRequest测试:----------------------password="+password+"---------------------------");
//构建一个响应头
//返回的http的响应体和响应头之间有2个换行rnrn
String respHeader="HTTP/1.1 200rn" +
"Content-Type: text/html;charset=utf-8rnrn";
String resp=respHeader+"<h1>hi lhTomcatV2</h1>";
System.out.println("==================lhtomcat2 返回的数据如下====================");
System.out.println("LhRequest测试:----------------------LhRequest="+lhRequest+"---------------------------");
System.out.println(resp);
//从socket返回数据给我们的浏览器/客户端 封装成http响应格式返回
OutputStream outputStream = socket.getOutputStream();
//resp.getBytes() 把字符串转成字节数组
outputStream.write(resp.getBytes());
//关闭流
outputStream.flush();
outputStream.close();
inputStream.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}finally {
//最后一定确保socket要关闭
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
LhTomcatV3_TestLhRequest.java
package xyz.lhweb.lhTomcat.handler;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* lh tomcat v3测试lh请求
*
* @author 罗汉 QQ;1072344372
* @date 2023/03/09
*/
public class LhTomcatV3_TestLhRequest {
public static void main(String[] args) throws IOException {
//1 创建ServletSocket 在8080端口监听
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("==========================LhTomcatV3_TestLhRequest在8080端口监听======================");
// 如果没有关闭就一直在等待监听
while (!serverSocket.isClosed()){
/**
* 1 等待浏览器/客户端的连接如果有连接来,就创建一个socket
* 2 这个socket就是服务端和浏览器/客户端之间的通道
*/
Socket socket = serverSocket.accept();
//3 创建一个线程对象,并且吧socket传给线程
new Thread(new LhRequestHandlerV3_TestLhRequest(socket)).start();
}
}
}
效果测试
在LhTomcatV3_TestLhRequest里面测试
浏览器输入:http://localhost:8080/LhTomcatV3_TestLhRequest?name=1&password=2
3.3.2 封装Response
代码展示
LhResponse.java
package xyz.lhweb.lhTomcat.http;
import java.io.OutputStream;
/**
* lh响应
* <p>
* 1 LhResponse对象可以封装OutputStream(是socket关联),
* 2 即可以通过 HspResponse对象 返回Http响应浏览器/客户端
* 3 LhResponse对象的作用等价于原生的servlet的 HttpServletResponse
*
* @author 罗汉 QQ;1072344372
* @date 2023/03/09
*/
public class LhResponse {
private OutputStream outputStream = null;
//写一个http的响应头 =>
//如果有兴趣,在编写更多的方法 比如setContentTupe
public static final String respHeader="HTTP/1.1 200 OKrn" +
"Content-Type: text/html;charset=utf-8rnrn";
// 在创建LhResponse时 传入的outputStream是和Socket关联的
public LhResponse(OutputStream outputStream) {
this.outputStream = outputStream;
}
// 当我们需要给浏览器返回数据时 可以通过LhResponse对象 的输出流完成
public OutputStream getOutputStream() {
return outputStream;
}
}
LhRequestHandlerV3_TestLhResponse.java
package xyz.lhweb.lhTomcat.handler;
import xyz.lhweb.lhTomcat.http.LhResponse;
import java.io.*;
import java.net.Socket;
/**
* lh请求处理程序
*
* @author 罗汉 QQ;1072344372
* @date 2023/03/09
*/
public class LhRequestHandlerV3_TestLhResponse implements Runnable{
//定义socket
private Socket socket=null;
public LhRequestHandlerV3_TestLhResponse(Socket socket) {
this.socket = socket;
}
@Override
public void run(
) {
// 这里我们可以对客户端/浏览器进行IO编程/交互
try {
InputStream inputStream = socket.getInputStream();
//把inputStream转成BufferedReader 方便按行读取
BufferedReader bufferedReader =
new BufferedReader(new InputStreamReader(inputStream, "utf-8"));
System.out.println("==================lhtomcatV3 接收到的数据如下====================");
String msg=null;
//如果我读取到的信息不为空,说明可以继续读取
while ((msg=bufferedReader.readLine())!=null){
//如果长度为0 "" 退出
if(msg.length()==0){
break;
}
System.out.println(msg);
}
//这里我们可以通过LhResponse对象,返回数据给浏览器/客户端
LhResponse lhResponse = new LhResponse(socket.getOutputStream());
String resp = LhResponse.respHeader + "<h1>lhResponse 返回的信息 hi LhRequestHandlerV3_TestLhResponse<h1>";
OutputStream outputStream = lhResponse.getOutputStream();
outputStream.write(resp.getBytes());
//关闭流
outputStream.flush();
outputStream.close();
inputStream.close();
socket.close();
} catch (IOException e) {
e.printStackTrace();
}finally {
//最后一定确保socket要关闭
if(socket!=null){
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
效果测试
3.3.2 封装servlet
代码展示
LhServletV3.java
package xyz.lhweb.lhTomcat.servlet;
import xyz.lhweb.lhTomcat.http.LhRequest;
import xyz.lhweb.lhTomcat.http.LhResponse;
import java.io.IOException;
import java.io.OutputStream;
/**
* lh servlet v3
*
* @author 罗汉 QQ;1072344372
* @date 2023/03/09
*/
public class LhServletV3 extends LhHttpServlet{
public LhServletV3() throws Exception {
init();
}
@Override
public void doGet(LhRequest req, LhResponse res) {
String name = req.getParameter("name");
String password = req.getParameter("password");
//outputStream和socket关联
OutputStream outputStream = res.getOutputStream();
String respHeader = LhResponse.respHeader+"<h1>姓名:"+name+"密码:"+password+"</h1>";
try {
outputStream.write(respHeader.getBytes());
outputStream.flush();
outputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void doPost(LhRequest req, LhResponse res) {
this.doGet(req, res);
}
@Override
public void init() throws Exception {
System.out.println("init方法被调用");
}
@Override
public void destroy() {
}
}
LhRequestHandlerV3_TestLhServlet.java
package xyz.lhweb.lhTomcat.handler;
import xyz.lhweb.lhTomcat.http.LhRequest;
import xyz.lhweb.lhTomcat.http.LhResponse;
import xyz.lhweb.lhTomcat.servlet.LhServletV3;
import java.io.*;
import java.net.Socket;
/**
* lh请求处理程序
*
* @author 罗汉 QQ;1072344372
* @date 2023/03/09
*/
public class LhRequestHandlerV3_TestLhServlet implements Runnable {
// 定义socket
private Socket socket = null;
public LhRequestHandlerV3_TestLhServlet(Socket socket) {
this.socket = socket;
}
@Override
public void run(
) {
// 这里我们可以对客户端/浏览器进行IO编程/交互
try {
InputStream inputStream = socket.getInputStream();
LhRequest lhRequest = new LhRequest(inputStream);
// 这里我们可以通过LhResponse对象,返回数据给浏览器/客户端
LhResponse lhResponse = new LhResponse(socket.getOutputStream());
// 创建LhServletV3对象 之后要用反射实现
LhServletV3 lhServletV3 = new LhServletV3();
lhServletV3.doGet(lhRequest, lhResponse);
} catch (Exception e) {
e.printStackTrace();
} finally {
// 最后一定确保socket要关闭
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
测试代码
LhTomcatV3_TestLhServletV3.java
package xyz.lhweb.lhTomcat.handler;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
/**
* lh tomcat v3测试lh请求
*
* @author 罗汉 QQ;1072344372
* @date 2023/03/09
*/
public class LhTomcatV3_TestLhServletV3 {
public static void main(String[] args) throws IOException {
//1 创建ServletSocket 在8080端口监听
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("==========================LhRequestHandlerV3_TestLhServlet在8080端口监听======================");
// 如果没有关闭就一直在等待监听
while (!serverSocket.isClosed()){
/**
* 1 等待浏览器/客户端的连接如果有连接来,就创建一个socket
* 2 这个socket就是服务端和浏览器/客户端之间的通道
*/
Socket socket = serverSocket.accept();
//3 创建一个线程对象,并且吧socket传给线程
new Thread(new LhRequestHandlerV3_TestLhServlet(socket)).start();
}
}
}
效果测试
浏览器输入:
3.3.3 容器设计
一定要记得将web,xml复制到target/classes下
代码展示
LhTomcatV3.java
package xyz.lhweb.lhTomcat;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import xyz.lhweb.lhTomcat.handler.LhRequestHandlerV3_Test;
import xyz.lhweb.lhTomcat.servlet.LhServletV3;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
/**
* lh tomcat v3
* 第三版,实现通过xml+反射来初始化容器
*
* @author 罗汉 QQ;1072344372
* @date 2023/03/16
*/
public class LhTomcatV3 {
public static void main(String[] args) {
LhTomcatV3 lhTomcatV3 = new LhTomcatV3();
lhTomcatV3.init();
// 启动LhTomcatV3容器
lhTomcatV3.run();
}
// 启动LhTomcatV3容器
public void run() {
try {
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("======================LhTomcatV3在8080监听==========================");
// 没有关闭掉就循环监听
while (!serverSocket.isClosed()) {
Socket socket = serverSocket.accept();
LhRequestHandlerV3_Test lhRequestHandlerV3_test = new LhRequestHandlerV3_Test(socket);
new Thread(lhRequestHandlerV3_test).start();
}
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 第一个容器
* 容器 servletMapping
* -ConcurrentHashMap
* -HashMap
* key -valueServletName 对应的实例
*/
public static final HashMap<String, LhServletV3> servletMapping = new HashMap<>();
/**
* 第一个容器
* 容器 servletUrlMapping
* -ConcurrentHashMap
* -HashMap
* key -valueurl -pattern ServletName
*/
public static final HashMap<String, String> servletUrlMapping = new HashMap<>();
// 初始化2个容器
public void init() {
// 用dom4j解析xml
String path = LhTomcatV3.class.getResource("/").getPath();
// System.out.println(path);
SAXReader saxReader = new SAXReader();
// web.xml 在target/classes 下
try {
Document document = saxReader.read(new File(path + "web.xml"));
// System.out.println(document);// 查看是否能找到web.xml
Element rootElement = document.getRootElement();// 拿到根元素 web-app
// 得到根元素下面的所有元素
List<Element> elements = rootElement.elements();
// 遍历并过滤
for (Element element : elements) {
if ("servlet".equalsIgnoreCase(element.getName())) {
// 这是一个servlet配置
// System.out.println("发现了servlet");
// 使用反射将serlvet实例放入到 servletMapping
Element servletName = element.element("servlet-name");
Element servletClass = element.element("servlet-class");
servletMapping.put(servletName.getText(), (LhServletV3) Class.forName(servletClass.getText().trim()).newInstance());
} else if ("servlet-mapping".equalsIgnoreCase(element.getName())) {
// 这是一个servlet-mapping
// System.out.println("发现了servlet-mapping");
Element servletName = element.element("servlet-name");
Element urlPattern = element.element("url-pattern");
// 注意 url作为key name作为值
servletUrlMapping.put(urlPattern.getText().trim(), servletName.getText());
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
// 验证 2个容器是否初始化成功
System.out.println("servletMapping: " + servletMapping);
System.out.println("servletUrlMapping " + servletUrlMapping);
}
}
LhServletV3.java
package xyz.lhweb.lhTomcat.servlet;
import xyz.lhweb.lhTomcat.http.LhRequest;
import xyz.lhweb.lhTomcat.http.LhResponse;
import java.io.IOException;
import java.io.OutputStream;
/**
* lh servlet v3
*
* @author 罗汉 QQ;1072344372
* @date 2023/03/09
*/
public class LhServletV3 extends LhHttpServlet{
public LhServletV3() throws Exception {
init();
}
@Override
public void doGet(LhRequest req, LhResponse res) {
String name = req.getParameter("name");
String password = req.getParameter("password");
//outputStream和socket关联
OutputStream outputStream = res.getOutputStream();
String respHeader = LhResponse.respHeader+"<h1>姓名:"+name+"密码:"+password+"-----------------lhServletV3-反射+xml创建"+"</h1>";
try {
outputStream.write(respHeader.getBytes());
outputStream.flush();
outputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void doPost(LhRequest req, LhResponse res) {
this.doGet(req, res);
}
@Override
public void init() throws Exception {
// System.out.println("init方法被调用");
}
@Override
public void destroy() {
}
}
LhRequestHandlerV3_Test
package xyz.lhweb.lhTomcat.handler;
import xyz.lhweb.lhTomcat.LhTomcatV3;
import xyz.lhweb.lhTomcat.http.LhRequest;
import xyz.lhweb.lhTomcat.http.LhResponse;
import xyz.lhweb.lhTomcat.servlet.LhHttpServlet;
import xyz.lhweb.lhTomcat.servlet.LhServletV3;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;
/**
* lh请求处理程序
*
* @author 罗汉 QQ;1072344372
* @date 2023/03/09
*/
public class LhRequestHandlerV3_Test implements Runnable {
// 定义socket
private Socket socket = null;
public LhRequestHandlerV3_Test(Socket socket) {
this.socket = socket;
}
@Override
public void run(
) {
// 这里我们可以对客户端/浏览器进行IO编程/交互
try {
LhRequest lhRequest = new LhRequest(socket.getInputStream());
// 这里我们可以通过LhResponse对象,返回数据给浏览器/客户端
LhResponse lhResponse = new LhResponse(socket.getOutputStream());
// 反射实现
// 1 得到uri=> servletUrlMapping 的url-pattern
String uri = lhRequest.getUri();
// key-uri value-servletName
String servletName = LhTomcatV3.servletUrlMapping.get(uri);
System.out.println("servletName:"+servletName);
// 2 通过uri->servletName->servlet实例
// 编译类型是LhHttpServlet ,!!!!!!!!!!!!!真正的运行类型是其子类
LhHttpServlet lhHttpServlet =
LhTomcatV3.servletMapping.get(servletName);
// 3 调用service方法 通过oop的动态绑定 调用运行类型的doGet(),doPost()
if(lhHttpServlet!=null){
lhHttpServlet.service(lhRequest, lhResponse);
}else {
//uri是瞎写的 不存在 没有这个servlet 返回404信息
String resp=LhResponse.respHeader+"<h1>404 not Foud</h1>";
OutputStream outputStream = lhResponse.getOutputStream();
outputStream.write(resp.getBytes());
outputStream.flush();
outputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 最后一定确保socket要关闭
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
效果展示
运行LhTomcatV3
浏览器输入:
http://localhost:8080/lhServletV3?name=luohan&password=123
http://localhost:8080/lhServletV99
第2个和第4个是请求icon的Null
3.4 v3版本完成进行测试
代码展示
LhServletV3Demo01.java
package xyz.lhweb.lhTomcat.servlet;
import xyz.lhweb.lhTomcat.http.LhRequest;
import xyz.lhweb.lhTomcat.http.LhResponse;
import java.io.IOException;
import java.io.OutputStream;
public class LhServletV3Demo01 extends LhServletV3{
public LhServletV3Demo01() throws Exception {
}
@Override
public void doGet(LhRequest req, LhResponse res) {
String a = req.getParameter("a");
String b = req.getParameter("b");
//outputStream和socket关联
OutputStream outputStream = res.getOutputStream();
String respHeader = LhResponse.respHeader+"<h1>a:"+a+"+"+b+"="+(Integer.parseInt(a)+Integer.parseInt(b))+"-----------------lhServletV3_反射+xml创建"+"</h1>";
try {
outputStream.write(respHeader.getBytes());
outputStream.flush();
outputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void doPost(LhRequest req, LhResponse res) {
this.doGet(req, res);
}
}
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!-- 配置自己设计的servlet
爆红不管他 自己设置的不识别
直接忽略
-->
<servlet>
<servlet-name>LhServletV3</servlet-name>
<servlet-class>xyz.lhweb.lhTomcat.servlet.LhServletV3</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LhServletV3</servlet-name>
<url-pattern>/lhServletV3</url-pattern>
</servlet-mapping>
<!--测试LhServletV3Demo01-->
<servlet>
<servlet-name>LhServletV3Demo01</servlet-name>
<servlet-class>xyz.lhweb.lhTomcat.servlet.LhServletV3Demo01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>LhServletV3Demo01</servlet-name>
<url-pattern>/lhServletV3Demo01</url-pattern>
</servlet-mapping>
</web-app>
LhTomcatV3.java
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package xyz.lhweb.lhTomcat;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import xyz.lhweb.lhTomcat.handler.LhRequestHandlerV3_Test;
import xyz.lhweb.lhTomcat.servlet.LhServletV3;
public class LhTomcatV3 {
public static final HashMap<String, LhServletV3> servletMapping = new HashMap();
public static final HashMap<String, String> servletUrlMapping = new HashMap();
public LhTomcatV3() {
}
public static void main(String[] args) {
LhTomcatV3 lhTomcatV3 = new LhTomcatV3();
lhTomcatV3.init();
lhTomcatV3.run();
}
public void run() {
try {
ServerSocket serverSocket = new ServerSocket(8080);
System.out.println("======================LhTomcatV3在8080监听==========================");
while(!serverSocket.isClosed()) {
Socket socket = serverSocket.accept();
LhRequestHandlerV3_Test lhRequestHandlerV3_test = new LhRequestHandlerV3_Test(socket);
(new Thread(lhRequestHandlerV3_test)).start();
}
} catch (IOException var4) {
throw new RuntimeException(var4);
}
}
public void init() {
String path = LhTomcatV3.class.getResource("/").getPath();
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read(new File(path + "web.xml"));
Element rootElement = document.getRootElement();
List<Element> elements = rootElement.elements();
Iterator var6 = elements.iterator();
while(var6.hasNext()) {
Element element = (Element)var6.next();
Element servletName;
Element urlPattern;
if ("servlet".equalsIgnoreCase(element.getName())) {
servletName = element.element("servlet-name");
urlPattern = element.element("servlet-class");
servletMapping.put(servletName.getText(), (LhServletV3)Class.forName(urlPattern.getText().trim()).newInstance());
} else if ("servlet-mapping".equalsIgnoreCase(element.getName())) {
servletName = element.element("servlet-name");
urlPattern = element.element("url-pattern");
servletUrlMapping.put(urlPattern.getText().trim(), servletName.getText());
}
}
} catch (Exception var10) {
throw new RuntimeException(var10);
}
System.out.println("servletMapping: " + servletMapping);
System.out.println("servletUrlMapping " + servletUrlMapping);
}
}
LhServletV3.java
package xyz.lhweb.lhTomcat.servlet;
import xyz.lhweb.lhTomcat.http.LhRequest;
import xyz.lhweb.lhTomcat.http.LhResponse;
import java.io.IOException;
import java.io.OutputStream;
/**
* lh servlet v3
*
* @author 罗汉 QQ;1072344372
* @date 2023/03/09
*/
public class LhServletV3 extends LhHttpServlet{
public LhServletV3() throws Exception {
init();
}
@Override
public void doGet(LhRequest req, LhResponse res) {
String name = req.getParameter("name");
String password = req.getParameter("password");
//outputStream和socket关联
OutputStream outputStream = res.getOutputStream();
String respHeader = LhResponse.respHeader+"<h1>姓名:"+name+"密码:"+password+"-----------------lhServletV3-反射+xml创建"+"</h1>";
try {
outputStream.write(respHeader.getBytes());
outputStream.flush();
outputStream.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
@Override
public void doPost(LhRequest req, LhResponse res) {
this.doGet(req, res);
}
@Override
public void init() throws Exception {
// System.out.println("init方法被调用");
}
@Override
public void destroy() {
}
}
LhRequestHandlerV3.java
package xyz.lhweb.lhTomcat.handler;
import xyz.lhweb.lhTomcat.LhTomcatV3;
import xyz.lhweb.lhTomcat.http.LhRequest;
import xyz.lhweb.lhTomcat.http.LhResponse;
import xyz.lhweb.lhTomcat.servlet.LhHttpServlet;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
/**
* lh请求处理程序
*
* @author 罗汉 QQ;1072344372
* @date 2023/03/09
*/
public class LhRequestHandlerV3 implements Runnable {
// 定义socket
private Socket socket = null;
public LhRequestHandlerV3(Socket socket) {
this.socket = socket;
}
@Override
public void run(
) {
// 这里我们可以对客户端/浏览器进行IO编程/交互
try {
LhRequest lhRequest = new LhRequest(socket.getInputStream());
// 这里我们可以通过LhResponse对象,返回数据给浏览器/客户端
LhResponse lhResponse = new LhResponse(socket.getOutputStream());
// 反射实现
// 1 得到uri=> servletUrlMapping 的url-pattern
String uri = lhRequest.getUri();
// key-uri value-servletName
String servletName = LhTomcatV3.servletUrlMapping.get(uri);
if (servletName==null){
servletName="null";
}
System.out.println("servletName:"+servletName);
// 2 通过uri->servletName->servlet实例
// 编译类型是LhHttpServlet ,!!!!!!!!!!!!!真正的运行类型是其子类
LhHttpServlet lhHttpServlet =
LhTomcatV3.servletMapping.get(servletName);
// 3 调用service方法 通过oop的动态绑定 调用运行类型的doGet(),doPost()
if(lhHttpServlet!=null){
lhHttpServlet.service(lhRequest, lhResponse);
}else {
//uri是瞎写的 不存在 没有这个servlet 返回404信息
String resp=LhResponse.respHeader+"<h1>404 not Foud</h1>";
OutputStream outputStream = lhResponse.getOutputStream();
outputStream.write(resp.getBytes());
outputStream.flush();
outputStream.close();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
// 最后一定确保socket要关闭
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
}
最终展示
浏览器输入:
http://localhost:8080/lhServletV3Demo01?a=1&b=2
http://localhost:8080/lhServletV88
三 、源码以及视频讲解
总结
手动实现 Tomcat 底层机制+ 自己设计 Servlet。模拟Tomcat底层实现并能调用我们自己设计的Servlet实现一个简单的小案例。
原文地址:https://blog.csdn.net/luohanisme/article/details/129283640
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.7code.cn/show_42930.html
如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!