目录
一、 手写实现迷你版Tomcat思路分析
二、MyTomcat 1.0 版本
2.1 手写实现迷你版Tomcat V1.0开发
- 创建一个maven工程
- maven 加上一个编译插件
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>11</source>
<target>11</target>
<encoding>utf-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
- 1.0 的代码
package com.lagou.server;
import java.io.IOException;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;
/**
* My tomcat 1.0
*/
public class Bootstrap {
// 配置的端口号
private int port = 8080;
public int getPort() {
return port;
}
public void setPort(int port) {
this.port = port;
}
/**
* MyTomcat 启动需要初始化展开的一些操作
*/
public void start() throws IOException {
/**
* 完成MyTomcat 1.0 版本
* 需求:浏览器请求 http:// localhost:8080 ,返回一个固定的字符串到页面 "Hello MyTomcat!!"
*/
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("====>>> My Tomcat" + port);
while (true){
Socket socket = serverSocket.accept();
// 有了socket,接收到请求,获取输入流
OutputStream outputStream = socket.getOutputStream();
outputStream.write("Hello MyTomcat!!".getBytes());
socket.close();
}
}
/**
* My Tomcat 的程序入口
*/
public static void main(String[] args) {
}
}
2.2 测试遇到的问题分析
- 测试程序
/**
* My Tomcat 的程序入口
*/
public static void main(String[] args) {
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.start();
} catch (IOException e) {
e.printStackTrace();
}
}
- 测试结果
- 分析
response应该 加上一个 类似上面的信息
2.3 手写实现迷你版Tomcat V1.0开发完成
/**
* http 协议工具类,主要是提供 响应头对应的信息,这里我们只提供 200 和 404 的情况
*/
public class HttpProtocolUtil {
private static String str404 = "<h1>404 not found</h1>";
/**
* 设置 请求为 200 的 head信息
* @param contentLength
* @return
*/
public static String getHttpHead200(Long contentLength){
return "HTTP/1.1 200 OK \n"+
"Content-Type:text/html \n"+
"Content-Length"+contentLength+"\n"+
"\r\n";
}
/**
* 设置 请求为 404 的 head信息
* @return
*/
public static String getHttpHead404(){
return "HTTP/1.1 404 NOT Found \n"+
"Content-Type:text/html \n"+
"Content-Length"+str404.length()+"\n"+
"\r\n" + str404;
}
}
/**
* MyTomcat 启动需要初始化展开的一些操作
*/
public void start() throws IOException {
/**
* 完成MyTomcat 1.0 版本
* 需求:浏览器请求 http:// localhost:8080 ,返回一个固定的字符串到页面 "Hello MyTomcat!!"
*/
ServerSocket serverSocket = new ServerSocket(port);
System.out.println("====>>> My Tomcat" + port);
while (true){
Socket socket = serverSocket.accept();
// 有了socket,接收到请求,获取输入流
OutputStream outputStream = socket.getOutputStream();
String data = "Hello MyTomcat!!";
outputStream.write((HttpProtocolUtil.getHttpHead200(data.length()) + data).getBytes()) ;
socket.close();
}
}
三、My Tomcat 2.0
3.1 流程分析
2.2、 手写实现迷你版Tomcat V2.0 Request封装
import java.io.IOException;
import java.io.InputStream;
/**
* 把请求信息封装成Request对象(根据InputStream输入流封装)
*/
public class Request {
private String method; //请求方式,比如 GET/POST
private String url; // 例如
private InputStream inputStream;
public Request(InputStream inputStream) throws IOException {
// 将传入的流 转化成字符串
this.inputStream = inputStream;
int count= 0 ;
while (count== 0){
count = inputStream.available();
}
byte[] bytes = new byte[count];
inputStream.read(bytes);
String inputStr = new String(bytes);
// 解析字符串
// 解析第一行请求头信息
String firstHeadStr = inputStr.split("\\n")[0];
String[] strings = firstHeadStr.split(" ");
this.method = strings[0];
this.url = strings[1];
System.out.println("====>>>method :" +this.method);
System.out.println("====>>>url :" +this.url);
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public InputStream getInputStream() {
return inputStream;
}
public void setInputStream(InputStream inputStream) {
this.inputStream = inputStream;
}
}
2.3、手写实现迷你版Tomcat V2.0封装Response
import com.lagou.utils.HttpProtocolUtil;
import com.lagou.utils.StaticResourceUtil;
import java.io.*;
/**
* 封装Response 对象 ,需要依赖于OutputStream
* 该对象 需要提供 核心方法 输出html
*/
public class Response {
private OutputStream outputStream;
public Response(OutputStream outputStream) {
this.outputStream = outputStream;
}
public OutputStream getOutputStream() {
return outputStream;
}
public void setOutputStream(OutputStream outputStream) {
this.outputStream = outputStream;
}
/**
*
*
* @param path
*/
public void outputHtml(String path) throws IOException {
// 获取静态资源文件的绝对路径
String absoluteResourcePath = StaticResourceUtil.getAbsolutePath(path);
File file = new File(absoluteResourcePath);
if(file.exists()){
StaticResourceUtil.outputStaticResource(new FileInputStream(file),outputStream);
}else {
// 输出 404
output(HttpProtocolUtil.getHttpHead404());
}
}
private void output(String content) throws IOException {
outputStream.write(content.getBytes());
}
}
2.4、手写实现迷你版Tomcat V2.0封装静态资源工具类
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class StaticResourceUtil {
/**
* 获取资源文件的绝对路径
*/
public static String getAbsolutePath(String path){
String absolutePath = StaticResourceUtil.class.getResource("/").getPath();
return absolutePath.replaceAll("\\\\","/")+path;
}
/**
* 读取静态资源文件输入流,通过输出流输出
* @param inputStream
* @param outputStream
*/
public static void outputStaticResource(InputStream inputStream, OutputStream outputStream) throws IOException {
int count = 0;
while (count == 0){
count = inputStream.available();
}
int resourceSize = count;
// 第一步 :输出http 请求头,
outputStream.write(HttpProtocolUtil.getHttpHead200(resourceSize).getBytes());
// 第二步 :读取输出具体内容
long written = 0 ; // 已经读取的内容长度
int byteSize = 1024; // 计划每次缓冲的长度
byte[] bytes = new byte[byteSize];
while (written < resourceSize){
if(written+ byteSize > resourceSize){
// 判断是否剩余未读取的长度大小不足一个1024 长度,那就按真是长度处理
byteSize = (int) (resourceSize - written); // 剩余的文件内容长度
bytes = new byte[byteSize];
}
inputStream.read(bytes);
outputStream.write(bytes);
outputStream.flush();
written += byteSize;
}
}
}
2.5、 手写实现迷你版Tomcat V2.0测试
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
Hello Minicat-static resource!!!
</body>
</html>
import com.lagou.pojo.Request;
import com.lagou.pojo.Response;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Bootstrap {
private Integer port = 8081;
/**
* 2.0 tomcat 的版本
* @throws IOException
*/
public void start() throws IOException {
ServerSocket serverSocket = new ServerSocket(port);
while (true){
Socket socket = serverSocket.accept();
// 通过输入流 获取请求信息
InputStream inputStream = socket.getInputStream();
Request request = new Request(inputStream);
Response response = new Response(socket.getOutputStream());
response.outputHtml(request.getUrl());
socket.close();
}
}
public static void main(String[] args) {
Bootstrap bootstrap = new Bootstrap();
try {
bootstrap.start();
} catch (IOException e) {
e.printStackTrace();
}
}
}
8080 的解析
三 手写实现迷你版Tomcat V3.0定义Serlvet
3.1、 Serlvet
import com.lagou.pojo.Request;
import com.lagou.pojo.Response;
public interface Servlet {
void init() throws Exception;
void destory() throws Exception;
void service(Request request, Response response) throws Exception;
}
import com.lagou.pojo.Request;
import com.lagou.pojo.Response;
public abstract class HttpServlet implements Servlet {
public abstract void doGet(Request request, Response response) throws Exception;
public abstract void doPost(Request request, Response response) throws Exception;
@Override
public void service(Request request, Response response) throws Exception {
if("GET".equalsIgnoreCase(request.getMethod())){
doGet(request,response);
}else {
doPost(request,response);
}
}
}
import com.lagou.pojo.Request;
import com.lagou.pojo.Response;
import com.lagou.utils.HttpProtocolUtil;
public class LagouServlet extends HttpServlet {
@Override
public void doGet(Request request, Response response) throws Exception {
String content ="<h1> LagouServlet get</h1>" ;
try {
response.output(HttpProtocolUtil.getHttpHead200(content.length())+content);
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void doPost(Request request, Response response) throws Exception {
String content ="<h1> LagouServlet post</h1>" ;
try {
response.output(HttpProtocolUtil.getHttpHead200(content.length())+content);
}catch (Exception e){
e.printStackTrace();
}
}
@Override
public void init() throws Exception {
System.out.println("init方法");
}
@Override
public void destory() throws Exception {
System.out.println("destory方法");
}
}
3.2 配置
web.xml
<?xml version="1.0" encoding="UTF-8" ?>
<web-app>
<servlet>
<servlet-name>lagou</servlet-name>
<servlet-class>com.lagou.servlet.LagouServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>lagou</servlet-name>
<url-pattern>/lagou</url-pattern>
</servlet-mapping>
</web-app>
3.3 解析
import com.lagou.pojo.Request;
import com.lagou.pojo.Response;
import com.lagou.servlet.HttpServlet;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class BootStrap {
private int port = 8082;
private Map<String, HttpServlet> servletMap = new HashMap<String,HttpServlet>(); // servlet 容器
public void loadServlet(){
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream("web.xml");
SAXReader saxReader = new SAXReader();
try{
Document document = saxReader.read(resourceAsStream);
Element rootElement = document.getRootElement();
List<Element> servlets = rootElement.selectNodes("//servlet");
for (int i = 0; i < servlets.size(); i++) {
Element element = servlets.get(i);
// <servlet-name>lagou</servlet-name>
Element servletNameEle = (Element)element.selectSingleNode("servlet-name");
String servletName= servletNameEle.getTextTrim();
// <servlet-class>com.lagou.server.lagouServlet</servlet-class>
Element servletClassEle = (Element)element.selectSingleNode("servlet-class");
String servletClass= servletClassEle.getTextTrim();
// 根据 servletName 找到 url-pattern
Element servletMapping = (Element) rootElement.selectSingleNode("/web-app/servlet-mapping[servlet-name='" +servletName + "']");
String urlPattern = servletMapping.selectSingleNode("url-pattern").getStringValue();
servletMap.put(urlPattern,(HttpServlet) Class.forName(servletClass).newInstance());
}
}catch (Exception e){
e.printStackTrace();
}
}
/**
* My Tomcat 3.0
* @throws IOException
*/
public void start() throws Exception {
loadServlet();
ServerSocket serverSocket = new ServerSocket(port);
while (true){
Socket socket = serverSocket.accept();
Request request = new Request(socket.getInputStream());
Response response = new Response(socket.getOutputStream());
// 静态资源
if(servletMap.get(request.getUrl()) == null){
response.outHtml(request.getUrl());
}else {
HttpServlet httpServlet = servletMap.get(request.getUrl());
httpServlet.service(request,response);
}
socket.close();
}
}
public static void main(String[] args) {
BootStrap bootStrap = new BootStrap();
try {
bootStrap.start();
} catch (Exception e) {
e.printStackTrace();
}
}
}
3.4、手写实现迷你版Tomcat V3.0开发完成及测试
/**
* My Tomcat 3.0
* @throws IOException
*/
public void start() throws Exception {
loadServlet();
ServerSocket serverSocket = new ServerSocket(port);
while (true){
Socket socket = serverSocket.accept();
Request request = new Request(socket.getInputStream());
Response response = new Response(socket.getOutputStream());
// 静态资源
if(servletMap.get(request.getUrl()) == null){
response.outHtml(request.getUrl());
}else {
HttpServlet httpServlet = servletMap.get(request.getUrl());
httpServlet.service(request,response);
}
socket.close();
}
}
public static void main(String[] args) {
BootStrap bootStrap = new BootStrap();
try {
bootStrap.start();
} catch (Exception e) {
e.printStackTrace();
}
}
四、My Tomcat 多线程的实现
4.1、 手写实现迷你版Tomcat多线程改造(不使用线程池)
import com.lagou.pojo.Request;
import com.lagou.pojo.Response;
import com.lagou.servlet.HttpServlet;
import java.net.Socket;
import java.util.Map;
public class RequestProcessor1 extends Thread {
private Socket socket;
private Map<String, HttpServlet> servletMap ;
public RequestProcessor1(Socket socket,Map<String, HttpServlet> servletMap){
this.socket = socket;
this.servletMap = servletMap;
}
@Override
public void run() {
try{
Request request = new Request(socket.getInputStream());
Response response = new Response(socket.getOutputStream());
// 静态资源
if(servletMap.get(request.getUrl()) == null){
Thread.sleep(100000);
response.outputHtml(request.getUrl());
}else {
HttpServlet httpServlet = servletMap.get(request.getUrl());
httpServlet.service(request,response);
}
socket.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
public void start() throws Exception {
ServerSocket serverSocket = new ServerSocket(port);
loadServlet();
while (true){
Socket socket = serverSocket.accept();
RequestProcessor1 requestProcessor1 = new RequestProcessor1(socket, servletMap);
requestProcessor1.start();
}
}
4.2、手写实现迷你版Tomcat多线程改造(使用线程池)
public void start() throws Exception {
ServerSocket serverSocket = new ServerSocket(port);
loadServlet();
// int corePoolSize,
// int maximumPoolSize,
// long keepAliveTime,
// TimeUnit unit,
// BlockingQueue<Runnable> workQueue,
// ThreadFactory
// threadFactory,
// RejectedExecutionHandler handler
int corePoolSize = 10;
int maximumPoolSize = 50 ;
long keepAliveTime = 100L;
TimeUnit unit = TimeUnit.SECONDS;
BlockingQueue<Runnable> workQueue = new ArrayBlockingQueue<>(50);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(corePoolSize,maximumPoolSize,keepAliveTime,unit,workQueue,threadFactory,handler);
while (true){
Socket socket = serverSocket.accept();
RequestProcessor requestProcessor = new RequestProcessor(socket, servletMap);
// 多线程 1 ===>不使用 线程池
// requestProcessor1.start();
// 多线程 2 ===>使用 线程池
threadPoolExecutor.execute(requestProcessor);
}
}