Hello大家好,本章我們自己實現一個簡易版的tomcat 。有問題可以聯絡我mr_beany@163.com。另求各路大神指點,感謝
程式的執行少不了伺服器的支援,而tomcat因其效能穩定,免費等優點,深受 Java 愛好者的喜愛並得到了部分軟體開發商的認可,為目前比較流行的 Web 應用伺服器。
那麼到底它是怎麼執行的呢?今天我們來實現一個簡化版tomcat來感受一下。
一:匯入解析xml的jar包
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>複製程式碼
二:編寫XML解析類
package com.example.demo.utils;
import java.util.List;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
/**
* @author zy
*/
public class XmlUtils {
/**
* 定義解析器和文件物件
*/
public SAXReader saxReader;
public Document document;
public XmlUtils(String path) {
//獲取解析器
saxReader = new SAXReader();
try {
//獲取文件物件
document = saxReader.read(path);
} catch (DocumentException e) {
e.printStackTrace();
}
}
/**
* 獲取節點下的所有節點
*
* @param name
* @return
*/
public List<Element> getNodes(String name) {
Element root = document.getRootElement();
return root.elements(name);
}
}
複製程式碼
三:實現Request和Response
其中Request是對瀏覽器的請求的封裝,而Response是對瀏覽器請求的響應,換而言之就是Request 用來取出請求資訊,而 Response 則用來新增要返回給瀏覽器的資訊。
建立Request.java
package com.example.demo.http;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
/**
* @author zy
*/
public class Request {
private String method;
private String url;
public Request(InputStream inputStream) throws IOException {
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
String[] methodAndUrl = bufferedReader.readLine().split(" ");
this.method = methodAndUrl[0];
this.url = methodAndUrl[1];
System.out.println("請求型別:"+ method);
System.out.println("請求路徑:"+ 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;
}
}複製程式碼
建立Response.java
package com.example.demo.http;
import java.io.OutputStream;
/**
* @author zy
*/
public class Response {
private OutputStream outputStream;
private String write;
public Response(OutputStream outputStream){
this.outputStream = outputStream;
}
public String getWrite() {
return write;
}
public void setWrite(String write) {
this.write = write;
}
}複製程式碼
四:實現Servlet
先做個介面
package com.example.demo.servlet;
import com.example.demo.http.Request;
import com.example.demo.http.Response;
/**
* @author zy
*/
public abstract class AbstractServlet {
public abstract void doGet(Request request, Response response);
public abstract void doPost(Request request, Response response);
}複製程式碼
再來實現自己的Servlet
package com.example.demo.servlet;
import com.example.demo.http.Request;
import com.example.demo.http.Response;
/**
* @author zy
*/
public class FirstServlet extends AbstractServlet {
@Override
public void doGet(Request request, Response response) {
response.setWrite("我的第一個Servlet");
}
@Override
public void doPost(Request request, Response response) {
this.doGet(request, response);
}
}複製程式碼
五:建立web.xml
這裡配置了請求路徑的servlet的對應關係,檔案放在resources資料夾下
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" version="2.5">
<servlet>
<servlet-name>first.html</servlet-name>
<servlet-class>com.example.demo.servlet.FirstServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>first.html</servlet-name>
<url-pattern>/first.html</url-pattern>
</servlet-mapping>
</web-app>複製程式碼
六,建立tomcat,初始化配置檔案
package com.example.demo.tomcat;
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 com.example.demo.utils.XmlUtils;
import org.dom4j.Element;
/**
* @author zy
*/
public class Tomcat {
/**
* 設定埠號
*/
private static final int PORT = 8080;
public static final HashMap<String, Object> SERVLET_MAPPING = new HashMap<>();
public static final HashMap<String, Object> SERVLET = new HashMap<>();
/**
* 控制伺服器啟動關閉
*/
public boolean tomcatStarBool = true;
private void init() {
InputStream io = null;
try {
System.out.println("載入配置檔案開始");
//讀取配置檔案
XmlUtils xml = new XmlUtils(XmlUtils.class.getResource("/")+"web.xml");
//將所有的類都儲存到容器中
List<Element> list = xml.getNodes("servlet");
for (Element element : list) {
SERVLET.put(element.element("servlet-name").getText(),
Class.forName(element.element("servlet-class").getText()).newInstance());
}
//對映關係建立
List<Element> list2 = xml.getNodes("servlet-mapping");
for (Element element : list2) {
SERVLET_MAPPING.put(element.element("url-pattern").getText(),
element.element("servlet-name").getText());
}
System.out.println("載入配置檔案結束");
} catch (Exception ex) {
ex.printStackTrace();
} finally {
if (io != null) {
try {
io.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}複製程式碼
七:建立處理請求任務的SocketProcess
package com.example.demo.tomcat;
import com.example.demo.http.Request;
import com.example.demo.http.Response;
import com.example.demo.servlet.AbstractServlet;
import java.io.OutputStream;
import java.net.Socket;
/**
* @author zy
*/
public class SocketProcess extends Thread{
protected Socket socket;
public SocketProcess(Socket socket){
this.socket = socket;
}
@Override
public void run() {
try {
Request request = new Request(socket.getInputStream());
Response response = new Response(socket.getOutputStream());
String servletName = (String) Tomcat.SERVLET_MAPPING.get(request.getUrl());
if(servletName!=null && !servletName.isEmpty()) {
//對映有的話找到對應的物件
AbstractServlet servlet = (AbstractServlet) Tomcat.SERVLET.get(servletName);
if(servlet!=null) {
servlet.doGet(request, response);
}else {
System.out.println("找不到對應的servlet");
}
}else {
System.out.println("找不到對應的servletMapping");
}
String res = response.getWrite();
OutputStream outputStream = socket.getOutputStream();
outputStream.write(res.getBytes("GBK"));
outputStream.flush();
outputStream.close();
}catch (Exception ex){
ex.printStackTrace();
}finally {
if (socket != null) {
try {
socket.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}複製程式碼
八:在tomcat.java中新增啟動方法
private void start() {
try {
ServerSocket serverSocket = new ServerSocket(PORT);
System.out.println("Tomcat 服務已啟動,地址:localhost ,埠:" + PORT);
this.init();
//持續監聽
do {
Socket socket = serverSocket.accept();
//處理任務
Thread thread = new SocketProcess(socket);
thread.start();
} while (tomcatStarBool);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Tomcat tomcat = new Tomcat();
tomcat.start();
}複製程式碼
九:測試
啟動在tomcat.java中執行void main方法
出現如下字樣代表啟動成功,然後位址列中輸入
http://localhost:8080/first.html
?????為什麼是這樣。。。。。。
啊!原來沒有新增響應頭資訊
Response.java中加入
/**
* 響應頭資訊
*/
public static final String RESPONSE_HEADER ="HTTP/1.1 200 \r\n"
+ "Content-Type: text/html\r\n"
+ "\r\n";複製程式碼
SocketProcess.java中
String res = response.getWrite();複製程式碼
改成
String res = Response.RESPONSE_HEADER + response.getWrite();複製程式碼
重新啟動,訪問
結尾
祝大家新年快樂。感謝支援!
其他文章:
碼雲地址: gitee.com/beany/mySpr…
GitHub地址: github.com/MyBeany/myS…