首頁>技術>

JAVA手寫tomcat,帶你了解tomcat的原理

1 建立一個簡單的servlet

程式碼示例:

package springmvc;

import java.io.IOException;

import javax.servlet.ServletException;

import javax.servlet.http.HttpServlet;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

public class MyServlet extends HttpServlet{

/**

* 一個最簡單的servlet

*/

private static final long serialVersionUID = 7683475760018217521L;

@Override

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

String data="這是我的第一個servlet!";

System.out.println(data);

}

}

web.xml中的標籤確定了servlet的訪問路徑

<!DOCTYPE web-app PUBLIC

"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"

"http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>

<display-name>Archetype Created Web Application</display-name>

<servlet>

<servlet-name>littleServlet</servlet-name>

<servlet-class>springmvc.MyServlet</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>littleServlet</servlet-name>

<url-pattern>/servlet</url-pattern>

</servlet-mapping>

</web-app>

servlet完成之後需要需要將他打包成專案(war/jar),並解壓完成,形成完整的專案目錄,供後續自己寫的tomcat使用。通常情況下,所有的專案都會放入tomcat的webapps目錄下。

開始手動編寫tomcat程式碼

程式碼示例

package springboot.tomcat;

import java.io.BufferedReader;

import java.io.IOException;

import java.io.InputStream;

import java.io.InputStreamReader;

import java.io.OutputStream;

import java.io.PrintWriter;

import java.io.UnsupportedEncodingException;

import java.net.ServerSocket;

import java.net.Socket;

import java.security.Principal;

import java.util.Collection;

import java.util.Enumeration;

import java.util.Locale;

import java.util.Map;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import javax.servlet.AsyncContext;

import javax.servlet.DispatcherType;

import javax.servlet.RequestDispatcher;

import javax.servlet.Servlet;

import javax.servlet.ServletContext;

import javax.servlet.ServletException;

import javax.servlet.ServletInputStream;

import javax.servlet.ServletOutputStream;

import javax.servlet.ServletRequest;

import javax.servlet.ServletResponse;

import javax.servlet.http.Cookie;

import javax.servlet.http.HttpServletRequest;

import javax.servlet.http.HttpServletResponse;

import javax.servlet.http.HttpSession;

import javax.servlet.http.HttpUpgradeHandler;

import javax.servlet.http.Part;

import springboot.util.ProjectUtil;

/**

* @author liuhongya328

*

*/

public class TomcatServer {

static int threads = 10;

private static ExecutorService pool = Executors.newCachedThreadPool();

/**

* @param args

*/

public static void main(String[] args) throws Exception{

//載入專案,獲取tomcat需要釋出的所有專案及專案下的所有servlet資訊

final Map<String, ProjectUtil.WebXml> projectIno = ProjectUtil.load();

ServerSocket serverSocket = new ServerSocket(8080);

System.out.println("Tomcat 啟動成功");

while(!serverSocket.isClosed()) {

//阻塞獲取新連線

Socket socket = serverSocket.accept();

pool.submit(()->{

//接收資料

InputStream inputStream = socket.getInputStream();;

//請求響應結果

OutputStream outputStream = socket.getOutputStream();

try {

System.out.println("收到請求:-------");

BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, "utf-8"));

String msg = null;

StringBuilder requestInfo = new StringBuilder();

while((msg = reader.readLine())!=null) {

if(msg.length()==0) {

break;

}

requestInfo.append(msg).append("\\r\\n");

}

System.out.println(requestInfo.toString()+"--------");

//請求需要訪問的servlet,這個servlet相當於業務程式碼的controller,是需要放入tomcat中啟動的專案,用原生的servlet便於從底層程式碼理解

//1.獲取請求方式 及請求路徑 GET /SpringMVC/servlet/littleServlet HTTP/1.1

String firstLine = requestInfo.toString().split("\\r\\n")[0];

String projectName = firstLine.split(" ")[1].split("/")[1];

String servletPath = firstLine.split(" ")[1].replace("/"+projectName, "");

//找到servlet對應的名稱--j2ee規範

String servletName = projectIno.get(projectName).servletMapping.get(servletPath).toString();

//找到servlet對應的例項

Servlet servlet = (Servlet) projectIno.get(projectName).servletInstances.get(servletName);

//將socket的inputsteam 轉成 request物件 ,如果想servlet接收和傳遞訊息,需要實現自己的HttpServletRequest/HttpServletResponse即可。

//這裡的建立僅作展示,實際沒有作用,並不會傳遞socket的資訊。

HttpServletRequest servletReuqest = createRequest();

HttpServletResponse servletResponse = createResponse();

//servlet的生命週期

servlet.service(servletReuqest, servletResponse);

outputStream.write("HTTP/1.1 200 OK\\r\\n".getBytes());

outputStream.write("Content-Length: 12\\r\\n\\r\\n".getBytes());

outputStream.write("Hello World!".getBytes());

outputStream.flush();

} catch (Exception e) {

// TODO Auto-generated catch block

e.printStackTrace();

}

socket.close();

return null;

});

}

serverSocket.close();

}

private static HttpServletRequest createRequest() {

return new HttpServletRequest() {

@Override

public Object getAttribute(String name) {

// TODO Auto-generated method stub

return null;

}

@Override

public Enumeration<String> getAttributeNames() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getCharacterEncoding() {

// TODO Auto-generated method stub

return null;

}

@Override

public void setCharacterEncoding(String env) throws UnsupportedEncodingException {

// TODO Auto-generated method stub

}

@Override

public int getContentLength() {

// TODO Auto-generated method stub

return 0;

}

@Override

public long getContentLengthLong() {

// TODO Auto-generated method stub

return 0;

}

@Override

public String getContentType() {

// TODO Auto-generated method stub

return null;

}

@Override

public ServletInputStream getInputStream() throws IOException {

// TODO Auto-generated method stub

return null;

}

@Override

public String getParameter(String name) {

// TODO Auto-generated method stub

return null;

}

@Override

public Enumeration<String> getParameterNames() {

// TODO Auto-generated method stub

return null;

}

@Override

public String[] getParameterValues(String name) {

// TODO Auto-generated method stub

return null;

}

@Override

public Map<String, String[]> getParameterMap() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getProtocol() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getScheme() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getServerName() {

// TODO Auto-generated method stub

return null;

}

@Override

public int getServerPort() {

// TODO Auto-generated method stub

return 0;

}

@Override

public BufferedReader getReader() throws IOException {

// TODO Auto-generated method stub

return null;

}

@Override

public String getRemoteAddr() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getRemoteHost() {

// TODO Auto-generated method stub

return null;

}

@Override

public void setAttribute(String name, Object o) {

// TODO Auto-generated method stub

}

@Override

public void removeAttribute(String name) {

// TODO Auto-generated method stub

}

@Override

public Locale getLocale() {

// TODO Auto-generated method stub

return null;

}

@Override

public Enumeration<Locale> getLocales() {

// TODO Auto-generated method stub

return null;

}

@Override

public boolean isSecure() {

// TODO Auto-generated method stub

return false;

}

@Override

public RequestDispatcher getRequestDispatcher(String path) {

// TODO Auto-generated method stub

return null;

}

@Override

public String getRealPath(String path) {

// TODO Auto-generated method stub

return null;

}

@Override

public int getRemotePort() {

// TODO Auto-generated method stub

return 0;

}

@Override

public String getLocalName() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getLocalAddr() {

// TODO Auto-generated method stub

return null;

}

@Override

public int getLocalPort() {

// TODO Auto-generated method stub

return 0;

}

@Override

public ServletContext getServletContext() {

// TODO Auto-generated method stub

return null;

}

@Override

public AsyncContext startAsync() throws IllegalStateException {

// TODO Auto-generated method stub

return null;

}

@Override

public AsyncContext startAsync(ServletRequest servletRequest, ServletResponse servletResponse)

throws IllegalStateException {

// TODO Auto-generated method stub

return null;

}

@Override

public boolean isAsyncStarted() {

// TODO Auto-generated method stub

return false;

}

@Override

public boolean isAsyncSupported() {

// TODO Auto-generated method stub

return false;

}

@Override

public AsyncContext getAsyncContext() {

// TODO Auto-generated method stub

return null;

}

@Override

public DispatcherType getDispatcherType() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getAuthType() {

// TODO Auto-generated method stub

return null;

}

@Override

public Cookie[] getCookies() {

// TODO Auto-generated method stub

return null;

}

@Override

public long getDateHeader(String name) {

// TODO Auto-generated method stub

return 0;

}

@Override

public String getHeader(String name) {

// TODO Auto-generated method stub

return null;

}

@Override

public Enumeration<String> getHeaders(String name) {

// TODO Auto-generated method stub

return null;

}

@Override

public Enumeration<String> getHeaderNames() {

// TODO Auto-generated method stub

return null;

}

@Override

public int getIntHeader(String name) {

// TODO Auto-generated method stub

return 0;

}

@Override

public String getMethod() {

//示例

return "GET";

}

@Override

public String getPathInfo() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getPathTranslated() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getContextPath() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getQueryString() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getRemoteUser() {

// TODO Auto-generated method stub

return null;

}

@Override

public boolean isUserInRole(String role) {

// TODO Auto-generated method stub

return false;

}

@Override

public Principal getUserPrincipal() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getRequestedSessionId() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getRequestURI() {

// TODO Auto-generated method stub

return null;

}

@Override

public StringBuffer getRequestURL() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getServletPath() {

// TODO Auto-generated method stub

return null;

}

@Override

public HttpSession getSession(boolean create) {

// TODO Auto-generated method stub

return null;

}

@Override

public HttpSession getSession() {

// TODO Auto-generated method stub

return null;

}

@Override

public String changeSessionId() {

// TODO Auto-generated method stub

return null;

}

@Override

public boolean isRequestedSessionIdValid() {

// TODO Auto-generated method stub

return false;

}

@Override

public boolean isRequestedSessionIdFromCookie() {

// TODO Auto-generated method stub

return false;

}

@Override

public boolean isRequestedSessionIdFromURL() {

// TODO Auto-generated method stub

return false;

}

@Override

public boolean isRequestedSessionIdFromUrl() {

// TODO Auto-generated method stub

return false;

}

@Override

public boolean authenticate(HttpServletResponse response) throws IOException, ServletException {

// TODO Auto-generated method stub

return false;

}

@Override

public void login(String username, String password) throws ServletException {

// TODO Auto-generated method stub

}

@Override

public void logout() throws ServletException {

// TODO Auto-generated method stub

}

@Override

public Collection<Part> getParts() throws IOException, ServletException {

// TODO Auto-generated method stub

return null;

}

@Override

public Part getPart(String name) throws IOException, ServletException {

// TODO Auto-generated method stub

return null;

}

@Override

public <T extends HttpUpgradeHandler> T upgrade(Class<T> httpUpgradeHandlerClass)

throws IOException, ServletException {

// TODO Auto-generated method stub

return null;

}

};

}

private static HttpServletResponse createResponse() {

return new HttpServletResponse() {

@Override

public String getCharacterEncoding() {

// TODO Auto-generated method stub

return null;

}

@Override

public String getContentType() {

// TODO Auto-generated method stub

return null;

}

@Override

public ServletOutputStream getOutputStream() throws IOException {

return null;

}

@Override

public PrintWriter getWriter() throws IOException {

// TODO Auto-generated method stub

return null;

}

@Override

public void setCharacterEncoding(String charset) {

// TODO Auto-generated method stub

}

@Override

public void setContentLength(int len) {

// TODO Auto-generated method stub

}

@Override

public void setContentLengthLong(long length) {

// TODO Auto-generated method stub

}

@Override

public void setContentType(String type) {

// TODO Auto-generated method stub

}

@Override

public void setBufferSize(int size) {

// TODO Auto-generated method stub

}

@Override

public int getBufferSize() {

// TODO Auto-generated method stub

return 0;

}

@Override

public void flushBuffer() throws IOException {

// TODO Auto-generated method stub

}

@Override

public void resetBuffer() {

// TODO Auto-generated method stub

}

@Override

public boolean isCommitted() {

// TODO Auto-generated method stub

return false;

}

@Override

public void reset() {

// TODO Auto-generated method stub

}

@Override

public void setLocale(Locale loc) {

// TODO Auto-generated method stub

}

@Override

public Locale getLocale() {

// TODO Auto-generated method stub

return null;

}

@Override

public void addCookie(Cookie cookie) {

// TODO Auto-generated method stub

}

@Override

public boolean containsHeader(String name) {

// TODO Auto-generated method stub

return false;

}

@Override

public String encodeURL(String url) {

// TODO Auto-generated method stub

return null;

}

@Override

public String encodeRedirectURL(String url) {

// TODO Auto-generated method stub

return null;

}

@Override

public String encodeUrl(String url) {

// TODO Auto-generated method stub

return null;

}

@Override

public String encodeRedirectUrl(String url) {

// TODO Auto-generated method stub

return null;

}

@Override

public void sendError(int sc, String msg) throws IOException {

// TODO Auto-generated method stub

}

@Override

public void sendError(int sc) throws IOException {

// TODO Auto-generated method stub

}

@Override

public void sendRedirect(String location) throws IOException {

// TODO Auto-generated method stub

}

@Override

public void setDateHeader(String name, long date) {

// TODO Auto-generated method stub

}

@Override

public void addDateHeader(String name, long date) {

// TODO Auto-generated method stub

}

@Override

public void setHeader(String name, String value) {

// TODO Auto-generated method stub

}

@Override

public void addHeader(String name, String value) {

// TODO Auto-generated method stub

}

@Override

public void setIntHeader(String name, int value) {

// TODO Auto-generated method stub

}

@Override

public void addIntHeader(String name, int value) {

// TODO Auto-generated method stub

}

@Override

public void setStatus(int sc) {

// TODO Auto-generated method stub

}

@Override

public void setStatus(int sc, String sm) {

// TODO Auto-generated method stub

}

@Override

public int getStatus() {

// TODO Auto-generated method stub

return 0;

}

@Override

public String getHeader(String name) {

// TODO Auto-generated method stub

return null;

}

@Override

public Collection<String> getHeaders(String name) {

// TODO Auto-generated method stub

return null;

}

@Override

public Collection<String> getHeaderNames() {

// TODO Auto-generated method stub

return null;

}

};

}

}

獲取專案資訊的工具類

package springboot.util;

import java.io.File;

import java.net.URL;

import java.net.URLClassLoader;

import java.util.HashMap;

import java.util.Map;

import java.util.Map.Entry;

import javax.servlet.Servlet;

/**

*

* 載入專案資訊

*

* */

public class ProjectUtil {

public static Map<String,WebXml> load() throws Exception{

final Map<String,WebXml> projetInfo = new HashMap<String,WebXml>();

//j2ee定義的是tomcat下的webapp目錄,本地我取自己打包的war包目錄

String webapps = "E:\\\\learning\\\\SpringMVC\\\\SpringMVC\\\\webapps";

//讀取tomcat釋出的所有的專案

File[] projects = new File(webapps).listFiles(projectName -> projectName.isDirectory());

for(File project : projects) {

//根據每個專案的web.xml讀取servlet資訊

WebXml webXml = new XMLConfigUtil().load(project.getPath()+"\\\\WEB-INF\\\\web.xml");

webXml.projectPath = project.getPath();

//類載入,載入class檔案-----war包或者jar包中解壓的class檔案

webXml.loadServlet();

projetInfo.put(project.getName(), webXml);

}

return projetInfo;

}

public class WebXml{

public String projectPath = null;

//查詢專案中所有的servlet

public Map<String,Object> servlets = new HashMap<String,Object>();

public Map<String,Object> servletMapping = new HashMap<String,Object>();

//例項物件

public Map<String,Object> servletInstances = new HashMap<String,Object>();

public void loadServlet() throws Exception {

//jvm載入class檔案

URL url = new URL("file:"+projectPath+"\\\\WEB-INF\\\\classes\\\\");

URLClassLoader classLoader = new URLClassLoader(new URL[] {url});

//載入servlet,建立servlet物件

for(Entry<String,Object> entry : servlets.entrySet()) {

//servlet的名稱

String servletName = entry.getKey();

//servlet類的完整路徑

String servletClassName = entry.getValue().toString();

//載入

Class<?> clazz = classLoader.loadClass(servletClassName);

//反射建立物件,這就是為什麼j2ee規範 servlet要繼承HttpServlet,

Servlet servlet = (Servlet) clazz.newInstance();

this.servletInstances.put(servletName, servlet);

}

}

}

}

解析web.xml的工具類

package springboot.util;

import java.io.IOException;

import java.util.HashMap;

import java.util.Map;

import javax.xml.parsers.ParserConfigurationException;

import javax.xml.parsers.SAXParser;

import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;

import org.xml.sax.SAXException;

import org.xml.sax.helpers.DefaultHandler;

import springboot.util.ProjectUtil.WebXml;

public class XMLConfigUtil extends DefaultHandler {

// 查詢專案中所有的servlet

public Map<String, Object> servlets = new HashMap<String, Object>();

public Map<String, Object> servletMapping = new HashMap<String, Object>();

// 例項物件

public Map<String, Object> servletInstances = new HashMap<String, Object>();

private String tag;// 儲存操作的標籤

private boolean isMapping = false;

private String currentServlet;

private String currentServletMapping;

public WebXml load(String path) throws SAXException, IOException, ParserConfigurationException {

// SAX解析

// 1、獲取SAX解析工廠

SAXParserFactory factory = SAXParserFactory.newInstance();

// 2、從解析工廠中獲取解析器

SAXParser parse = factory.newSAXParser();

XMLConfigUtil handler = new XMLConfigUtil();

// 5、解析

parse.parse(path, this);

// 6、獲取資料

ProjectUtil.WebXml webXMl = new ProjectUtil().new WebXml();

webXMl.servlets = this.servlets;

webXMl.servletMapping = this.servletMapping;

return webXMl;

}

@Override

public void startDocument() throws SAXException {

}

@Override

public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {

if (null != qName) {

tag = qName;// 儲存標籤名

if (tag.equals("servlet")) {

isMapping = false;

} else if (tag.equals("servlet-mapping")) {

isMapping = true;

}

}

}

@Override

public void characters(char[] ch, int start, int length) throws SAXException {

String contents = new String(ch, start, length).trim();

if (null != contents) {// 處理空的問題

if (isMapping) {// 操作servlet-mapping

if (tag.equals("servlet-name")) {

currentServletMapping = contents;

} else if (tag.equals("url-pattern")) {

String urlPattern = contents;

servletMapping.put(urlPattern, currentServlet);

}

} else {// 操作servlet

if (tag.equals("servlet-name")) {

currentServlet = contents;

currentServletMapping = contents;

} else if (tag.equals("servlet-class")) {

String servletClass = contents;

servlets.put(currentServlet, servletClass);

}

}

}

}

}

說明

本次程式碼沒有實現request和response是的互動,返回都是寫死的outputStream.write(“Hello World!”.getBytes())。

如果需要實現,需要寫自己的request和response。

原文連結:/file/2019/11/20/20191120164709_2943.jpg

  • BSA-TRITC(10mg/ml) TRITC-BSA 牛血清白蛋白改性標記羅丹明
  • Web前端程式設計師開發應必備的編碼原則