新闻宣传培训网站内容建设,杭州网站建设哪家最好,怎么自己学做电商,网站开发自学难吗目录
一、Tomcat底层整体架构 1.简介 : 2.分析图 : 3.基于Socket开发服务端的流程 : 4.打通服务器端和客户端的数据通道 :
二、多线程模型的实现 1.思路分析 : 2.处理HTTP请求 : 3.自定义Tomcat :
三、自定义Servlet规范 1. HTTP请求和响应 : 1 CyanServletRequest …目录
一、Tomcat底层整体架构 1.简介 : 2.分析图 : 3.基于Socket开发服务端的流程 : 4.打通服务器端和客户端的数据通道 :
二、多线程模型的实现 1.思路分析 : 2.处理HTTP请求 : 3.自定义Tomcat :
三、自定义Servlet规范 1. HTTP请求和响应 : 1° CyanServletRequest 2° CyanServletResponse 2.Servlet规范 : 1° CyanServlet 2° CyanHttpServlet 3° CyanLoginServlet 3.容器实现 : 1° 思路分析 2° web.xml配置文件 3° 最终版自定义Tomcat 4° 最终版自定义线程类 5° 容器启动测试 一、Tomcat底层整体架构 1.简介 : Tomcat 有三种运行模式 (BIO[阻塞], NIO[非阻塞], APR)这里采用 BIO 线程模型来模拟实现。 2.分析图 : 如下图所示 : 浏览器请求servlet资源后Tomcat底层会通过Socket网络编程来接收请求每次请求都会创建一个新的线程去调用相应的Web资源并返回。 3.基于Socket开发服务端的流程 : 如下图所示 : 通过获取的Socket对象来获取Socket对象对应的字节输入流和字节输出流。 可以利用对象转换流将字节流转换为字符流对象(InputStreamReader实现了Reader抽象类)然后再通过BufferedReader的包装将节点流转换成包装流(处理流)。 4.打通服务器端和客户端的数据通道 : PS : Maven配置Web应用运行出现jakarta.servlet.ServletException: 因为tomcat10之后不是javax.servlet而是jakarta.servlet所以Web的依赖应该换成如下所示 : (pom.xml配置文件
!--jar包的依赖--dependencygroupIdjakarta.servlet/groupIdartifactIdjakarta.servlet-api/artifactIdversion5.0.0/versionscopeprovided/scope/dependency!--jsp的依赖--dependencygroupIdjakarta.servlet.jsp/groupIdartifactIdjakarta.servlet.jsp-api/artifactIdversion3.0.0/versionscopeprovided/scope/dependency MyTomcat类代码如下 : 服务端自定义的Tomcat)
package tomcat;import java.io.*;
import java.net.ServerSocket;
import java.net.Socket;/**服务器端*/
public class MyTomcat {public static void main(String[] args) throws IOException {ServerSocket serverSocket new ServerSocket(8080);System.out.println(Tomcat在8080端口进行监听...);System.out.println(--------------------------------------);while (!serverSocket.isClosed()) {//获取Socket对象Socket socket serverSocket.accept();//接收来自浏览器端的信息InputStream inputStream socket.getInputStream();BufferedReader bufferedReader new BufferedReader(new InputStreamReader(inputStream, UTF-8));String content null; //局部变量在使用前必须赋初值。while ((content bufferedReader.readLine()) ! null) {//判断content是否是空串儿if (content.length() 0) {break;}System.out.println(content);}//服务器向浏览器回送消息OutputStream outputStream socket.getOutputStream();//设置一个HTTP响应包的响应头// /r/n表示换行String respHeader HTTP/1.1 200\r\n Content-Type: text/html;charsetutf-8\r\n\r\n;//设置HTTP响应的响应体String respBody respHeader h1你好/h1;/*注意这里不能使用字符包装流来写数据!!!。*/outputStream.write(respBody.getBytes());System.out.println(--------------------------------------);System.out.println(respBody);//释放资源outputStream.flush();outputStream.close();inputStream.close();socket.close();
// serverSocket.close();}}
}在浏览器地址栏访问本机8080端口。 login.html代码如下 :
!DOCTYPE html
html langen
headmeta charsetUTF-8titlelogin/titlestyletable, tr, td {border:2px cornflowerblue solid;border-collapse: collapse;padding: 10px;background-color: lightcyan}#tr01 {text-align: center}/style
/head
body
form action/Cyan_Tomcat/login methodsgettabletrth colspan2User Logging/th/trtrtdUsername: /tdtdinput typetext nameusername//td/trtrtdPassword: /tdtdinput typepassword namepassword//td/trtr idtr01tdinput typesubmit valueSubmit//tdtdinput typereset valueReset//td/tr/table
/form
/body
/html LoginServlet类代码如下 :
package servlet;import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;/*** author : Cyan_RA9* version : 21.0*/
WebServlet(urlPatterns {/login})
public class LoginServlet extends HttpServlet {Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {System.out.println(LoginServlets doPost is invoked~);resp.setContentType(text/html; charsetutf-8);PrintWriter writer resp.getWriter();req.setCharacterEncoding(utf-8);String username req.getParameter(username);String password req.getParameter(password);if (Cyan.equals(username) 123.equals(password)) {writer.print(h1登录成功/h1);} else {writer.print(h1登录失败请重新尝试/h1);}}Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}
}运行效果 : 如果以Web工程配置好的Tomcat运行就会按照LoginServlet类的代码逻辑来处理业务如下图所示 : (GIF) 如果以自定义的Tomcat运行就会以MyTomcat类中的代码逻辑来处理业务。 如下图所示 : (GIF) 二、多线程模型的实现 1.思路分析 : 当服务器端接收到浏览器端的HTTP请求后启动一个新的线程令该线程持有浏览器对应的Socket对象完成线程和浏览器的对接。 可通过实现Runnable接口的方式定义线程类HttpRequestHandler线程对象用于处理来自浏览器的HTTP请求。 2.处理HTTP请求 : 线程类HttpRequestHandler类代码如下 :
package tomcat.handler;import tomcat.http.CyanServletRequest;
import tomcat.http.CyanServletResponse;
import tomcat.servlet.CyanLoginServlet;import java.io.*;
import java.net.Socket;public class HttpRequestHandler implements Runnable {private Socket socket;public HttpRequestHandler(Socket socket) {this.socket socket;}Overridepublic void run() {try {//接收客户端的信息InputStream inputStream socket.getInputStream();/* 以下代码已在CyanServletRequest类中实现BufferedReader bufferedReader new BufferedReader(new InputStreamReader(inputStream, utf-8));//BIO每次请求都对应一个新的线程System.out.println(当前线程 Thread.currentThread().getName());String content null; //局部变量在使用前必须赋初值。while ((content bufferedReader.readLine()) ! null) {//判断是否读到了空字符串if (content.length() 0) {break;}System.out.println(content);}
*///Die first and live second//获取客户端的信息利用了CyanServletRequest中封装好的方法//以下代码已在CyanLoginServlet中实现
/* CyanServletRequest cyanServletRequest new CyanServletRequest(inputStream);String username cyanServletRequest.getParameter(username);String password cyanServletRequest.getParameter(password);System.out.println(username username);System.out.println(password password);System.out.println(cyanServletRequest);*///给客户端回送信息//以下代码已在CyanLoginServlet类中实现。
/* CyanServletResponse cyanServletResponse new CyanServletResponse(socket.getOutputStream());String resp CyanServletResponse.respHeader h1CyanServletResponse!/h1;OutputStream outputStream cyanServletResponse.getOutputStream();outputStream.write(resp.getBytes());outputStream.flush();outputStream.close();*/CyanServletRequest cyanServletRequest new CyanServletRequest(inputStream);CyanServletResponse cyanServletResponse new CyanServletResponse(socket.getOutputStream());CyanLoginServlet cyanLoginServlet new CyanLoginServlet();cyanLoginServlet.doPost(cyanServletRequest, cyanServletResponse);//释放资源inputStream.close();socket.close();/* 以下代码已在CyanServletResponse类中实现 :String respHeader HTTP/1.1 200\r\n Content-Type: text/html;charsetutf-8\r\n\r\n;String respHttp respHeader h1Cyan_RA9/h1;System.out.println(-----------------------------------------------);System.out.println(回送的信息如下(回显));System.out.println(respHttp);OutputStream outputStream socket.getOutputStream();outputStream.write(respHttp.getBytes());//释放资源outputStream.flush();outputStream.close();inputStream.close();socket.close(); */} catch (IOException e) {throw new RuntimeException(e);} finally {//确保Socket关闭if (socket ! null) {try {socket.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}3.自定义Tomcat : 在MyTomcat_EX类中实现线程的分发。 MyTomcat_EX类代码如下 :
package tomcat;import tomcat.handler.HttpRequestHandler;import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;/*** author : Cyan_RA9* version : 21.0*/
public class MyTomcat_EX {public static void main(String[] args) throws IOException {ServerSocket serverSocket new ServerSocket(8080);System.out.println(MyTomcat_EX在8080端口进行监听...);while (!serverSocket.isClosed()) {Socket socket serverSocket.accept();HttpRequestHandler httpRequestHandler new HttpRequestHandler(socket);Thread thread new Thread(httpRequestHandler);thread.start();}}
}运行效果 : (GIF) 三、自定义Servlet规范 1. HTTP请求和响应 : 1° CyanServletRequest CyanServletRequest类的作用等同于原始的HttpServletRequest该类用于封装HTTP请求中的数据eg : method, URI, 以及表单数据的参数列表等。 CyanServletRequest类代码如下 :
package tomcat.http;import java.io.*;
import java.util.HashMap;/*** author : Cyan_RA9* version : 21.0* function : like the original HttpServletRequest.*/
public class CyanServletRequest {private String method;private String URI;private HashMapString, String parametersMapping new HashMap();private InputStream inputStream;/*此处传入的InputStream对象是和Socket关联的InputStream.*/public CyanServletRequest(InputStream inputStream) {this.inputStream inputStream;//完成对HTTP请求数据的封装this.init();}private void init() {System.out.println(\nCyanServletRequests init is invoked~);try {//注意转换流的形参列表BufferedReader bufferedReader new BufferedReader(new InputStreamReader(inputStream, utf-8));//首先读取HTTP请求的请求行/* eg : GET /Cyan/cyan.html HTTP/1.1 */String requestLine bufferedReader.readLine();String[] requestLineArr requestLine.split( );//获取methodmethod requestLineArr[0];//获取URIint index requestLineArr[1].indexOf(?);if (index -1) { //if判断成立说明请求行中没有参数列表URI requestLineArr[1];} else {URI requestLineArr[1].substring(0, index);//获取参数列表String parameters requestLineArr[1].substring(index 1);String[] parameterPairs parameters.split();//兼容性处理防止?后啥都没有。if (null ! parameterPairs !.equals(parameterPairs)) {for (String parameterPair : parameterPairs) {String[] parameter parameterPair.split();if (parameter.length 2) { //判断是否为一对完整的namevalue.parametersMapping.put(parameter[0],parameter[1]);}}}}//!!! 直接关闭Socket关联的InputStream会引起Socket的关闭。//inputStream.close();} catch (UnsupportedEncodingException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);}}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;}//重要public String getParameter(String name) {if (parametersMapping.containsKey(name)) { //注意此处API的使用return parametersMapping.get(name);} else {return null;}}Overridepublic String toString() {return CyanServletRequest{ method method \ , URI URI \ , parametersMapping parametersMapping };}
}CyanServletRequest类测试运行效果如下GIF : 2° CyanServletResponse CyanServletResponse类的作用等同于原始的HttpServletResponse用于封装HTTP响应的相关信息。 CyanServletResponse类代码如下 :
package tomcat.http;import java.io.OutputStream;/*** author : Cyan_RA9* version : 21.0* function : like the original HttpServletResponse*/
public class CyanServletResponse {private OutputStream outputStream;//设置一个HTTP响应头public static final String respHeader HTTP/1.1 200\r\n Content-Type: text/html;charsetutf-8\r\n\r\n;//传入与Socket关联的OutputStream对象public CyanServletResponse(OutputStream outputStream) {this.outputStream outputStream;}public OutputStream getOutputStream() {return outputStream;}
}运行测试如下GIF图 2.Servlet规范 : 1° CyanServlet CyanServlet仅保留原生Servlet的init, destroy, service方法其中service方法供将来CyanServlet的抽象实现类CyanHttpServlet去重写。注意service方法的形参列表要使用自定义的CyanServletRequest 和 CyanServletResponse. CyanServlet接口代码如下 :
package tomcat.servlet;import tomcat.http.CyanServletRequest;
import tomcat.http.CyanServletResponse;import java.io.IOException;public interface CyanServlet {void init() throws Exception;void service(CyanServletRequest req, CyanServletResponse resp) throws IOException;void destroy();
}2° CyanHttpServlet CyanHttpServlet的作用类似于原生的HttpServlet在CyanHttpServlet中实现CyanServlet接口中的service方法在service方法中要对HTTP请求的method类型进行判断。 CyanHttpServlet抽象类代码如下 :
package tomcat.servlet;import tomcat.http.CyanServletRequest;
import tomcat.http.CyanServletResponse;import java.io.IOException;public abstract class CyanHttpServlet implements CyanServlet{Overridepublic void service(CyanServletRequest req, CyanServletResponse resp) throws IOException {//忽略大小写if (GET.equalsIgnoreCase(req.getMethod())) {this.deGet(req, resp);} else if (POST.equalsIgnoreCase(req.getMethod())) {this.doPost(req, resp);}}//模板设计模式public abstract void deGet(CyanServletRequest req, CyanServletResponse resp);public abstract void doPost(CyanServletRequest req, CyanServletResponse resp);
}3° CyanLoginServlet CyanLoginServlet是一个简单的servlet实例用于继承CyanHttpServlet抽象类并实现CyanHttpServlet类中的doGet和doPost抽象方法。之后在HttpRequestHandler线程类中先尝试直接调用CyanLoginServlet实例。 CyanLoginServlet代码如下 :
package tomcat.servlet;import tomcat.http.CyanServletRequest;
import tomcat.http.CyanServletResponse;import java.io.IOException;
import java.io.OutputStream;public class CyanLoginServlet extends CyanHttpServlet{Overridepublic void deGet(CyanServletRequest req, CyanServletResponse resp) {doPost(req, resp);}Overridepublic void doPost(CyanServletRequest req, CyanServletResponse resp) {String username req.getParameter(username);String password req.getParameter(password);//获取与当前Socket相关联的OutputStream对象OutputStream outputStream resp.getOutputStream();String respInfo CyanServletResponse.respHeader h1username username /h1br/ h1password password /h1 br/ h3Cyan_RA9/h3;try {outputStream.write(respInfo.getBytes());outputStream.flush();outputStream.close();} catch (IOException e) {throw new RuntimeException(e);}}Overridepublic void init() throws Exception {}Overridepublic void destroy() {}
}此外还需要更新HttpServletHandler类中的类型将已封装好的代码注释掉HttpServletHandler类已更新在上文“多线程模型实现”的HTTP请求处理模块。 运行效果如下图所示 : 3.容器实现 : 1° 思路分析 Tomcat中维护有至少两个大的HashMap容器。以web.xml配置文件的方式为例其中一个HashMap容器key存放url-patternvalue存放servlet-name另一个HashMap容器key存放servlet-namevalue存放通过servlet-class来反射生成的servlet实例。 2° web.xml配置文件 web.xml配置文件如下 :
!DOCTYPE web-app PUBLIC-//Sun Microsystems, Inc.//DTD Web Application 2.3//ENhttp://java.sun.com/dtd/web-app_2_3.dtd !--xmlnshttp://xmlns.jcp.org/xml/ns/javaeexmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsdversion4.0--
!--IDEA报错——因为这是我们自定义的servletIDEA无法识别无所谓继续用--
web-appdisplay-nameArchetype Created Web Application/display-nameservletservlet-nameCyanLoginServlet/servlet-nameservlet-classtomcat.servlet.CyanLoginServlet/servlet-class/servletservlet-mappingservlet-nameCyanLoginServlet/servlet-nameurl-pattern/cyanLogin/url-pattern/servlet-mapping
/web-app3° 最终版自定义Tomcat 在MyTomcat_Pro类中定义两个CurrentHashMap对象定义init方法完成对两个CurrentHashMap对象的初始化使用Dom4J读取web.xml配置文件。 首先需要在Maven的pom.xml配置文件中引入dom4j依赖如下图所示 : 然后将web.xml配置文件拷贝到/target/classes/目录下一份如下图所示 : MyTomcat_Pro代码如下 :
package tomcat;import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import tomcat.handler.HttpRequestHandler;
import tomcat.servlet.CyanHttpServlet;import java.io.File;
import java.io.IOException;
import java.lang.reflect.Constructor;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;/*** The final own custom Tomcat.*/
public class MyTomcat_Pro {//Tomcat维护的第一个容器//String -- servlet-name//CyanHttpServlet -- 可存放它的子类(即各种servlet实例)public static final ConcurrentHashMapString, CyanHttpServlet servletMapping new ConcurrentHashMap();//Tomcat维护的第二个容器//String -- url-patterns//String -- servlet-namepublic static final ConcurrentHashMapString, String servletURLMapping new ConcurrentHashMap();public static void main(String[] args) {MyTomcat_Pro myTomcat_pro new MyTomcat_Pro();myTomcat_pro.init();myTomcat_pro.run();}public void run() {try {ServerSocket serverSocket new ServerSocket(8080);System.out.println(MyTomcat_Pro在8080端口进行监听...);while (!serverSocket.isClosed()) {Socket socket serverSocket.accept();Thread thread new Thread(new HttpRequestHandler(socket));thread.start();}} catch (IOException e) {throw new RuntimeException(e);}}public void init() {String path MyTomcat_Pro.class.getResource(/).getPath();//path /javaProject/Servlet/Cyan_Tomcat/target/classes///使用Dom4J技术解析web.xml文件SAXReader saxReader new SAXReader();try {//注意文件名Document document saxReader.read(new File(path web.xml));System.out.println(document document);//获取根元素web-appElement rootElement document.getRootElement();//得到根元素下面的所有子元素ListElement elements rootElement.elements();//遍历并判断for (Element element : elements) {if (servlet.equals(element.getName())) {Element servlet_name element.element(servlet-name);Element servlet_class element.element(servlet-class);//反射机制创建servlet实例 (注意getText()方法的使用)Class? clazz Class.forName(servlet_class.getText().trim());Constructor? constructor clazz.getConstructor();CyanHttpServlet o (CyanHttpServlet) constructor.newInstance();servletMapping.put(servlet_name.getText(), o);} else if (servlet-mapping.equals(element.getName())) {Element url_pattern element.element(url-pattern);Element servlet_name element.element(servlet-name);servletURLMapping.put(url_pattern.getText(), servlet_name.getText());}}} catch (Exception e) {throw new RuntimeException(e);}}
}4° 最终版自定义线程类
package tomcat.handler;import tomcat.MyTomcat_Pro;
import tomcat.http.CyanServletRequest;
import tomcat.http.CyanServletResponse;
import tomcat.servlet.CyanHttpServlet;
import tomcat.servlet.CyanLoginServlet;import java.io.*;
import java.net.Socket;public class HttpRequestHandler implements Runnable {private Socket socket;public HttpRequestHandler(Socket socket) {this.socket socket;}Overridepublic void run() {try {//接收客户端的信息CyanServletRequest cyanServletRequest new CyanServletRequest(socket.getInputStream());CyanServletResponse cyanServletResponse new CyanServletResponse(socket.getOutputStream());String uri cyanServletRequest.getURI();System.out.println(uri uri);String servlet_name MyTomcat_Pro.servletURLMapping.get(uri);/*这里的servlet_name可能为空。解决方式一 : 将CurrentHashMap替换为HashMap解决方式二 : 增加一个是否为null的判断。*/if (servlet_name null) {servlet_name ;}//多态 -- 动态绑定CyanHttpServlet cyanHttpServlet MyTomcat_Pro.servletMapping.get(servlet_name);if (cyanHttpServlet ! null) { //判断是否正常得到servlet实例cyanHttpServlet.service(cyanServletRequest, cyanServletResponse);} else {//如果没有找到servlet返回404String resp CyanServletResponse.respHeader h1404 Not Found!/h1;OutputStream outputStream cyanServletResponse.getOutputStream();outputStream.write(resp.getBytes());outputStream.flush();outputStream.close();}//释放资源socket.close();} catch (IOException e) {throw new RuntimeException(e);} finally {//确保Socket关闭if (socket ! null) {try {socket.close();} catch (IOException e) {throw new RuntimeException(e);}}}}
}5° 容器启动测试 如下图所示GIF