JAVA手写tomcat,带你了解tomcat的原理,附代码

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。
代码注释很详细,如有疑问,欢迎交流!

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值