Tomcat源码分析

main项目与web项目

main方法是项目的入口,通过main方法启动项目,而web项目是没有main方法,如何让web项目启动起来,这时候就需要tomcat了,tomcat是一个servlet容器,处理http请求,把开发好的类打包成war包,然后放在tomcat的webapps下面,tomcat会自己解压war包,并且去运行程序

手撕Tomcat

原理

http协议实际是使用的TCP协议,底层是socket实现的

基本思路

背景

  1. tomcat--中间件
  2. web无main方法
  3. java--反射实现类动态加载
  4. web项目是通过http协议 tcp--socket

步骤

分析

tomcat需要main方法

tomcat需要监听本机上的某个端口

tomcat需要抓取此端口上来自客户端连接并且获取请求调用的方法与参数

tomcat需要根据请求调用方法,动态加载方法所在的类,完成类的实例化并通过该实例获取方法最终将请求传入方法

将结果返回给客户端(jsp/json/html/xml)

详细步骤

  1. 提供服务类Server类

  2. 处理请求信息Handler类

  3. 封装Request Response

  4. 实现Servlet

    HttpServlet抽象类,HttpServlet的实现类,web.xml文件的Servlet类,web.xml文件的ServletMapping类

  5. 分发请求 doGet或doPost或404或500

  6. 返回响应结果 response.write()

package com.yy.tomcat;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Server {
    private static ServerSocket serverSocket;
    //JAVA中对线程池定义的一个接口
    private static ExecutorService executorService;
    //线程池最大连接数
    private final static int POOL_SIZE=15;
    private static int port=8090;
    public static void start(){
    try {
        serverSocket=new ServerSocket(port);
        Socket socket;
        System.out.println("starting"+port);
        //通过看源码确定newFixedThreadPool方法返回的类ThreadPoolExecutor(实现了ExecutorService接口)
        executorService = Executors.newFixedThreadPool(POOL_SIZE);
        while(true){
            socket = serverSocket.accept();
            //放进execute的线程会自动执行start方法
            executorService.execute(new Handler(socket));
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
}
    public static void main(String[] args) {
        start();
    }
}

package com.yy.tomcat;
import java.io.*;
import java.net.Socket;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class Handler implements Runnable {
    private Socket socket;
    public Handler(Socket socket) {
        this.socket = socket;
    }
    @Override
    public void run() {
        //处理请求信息
        PrintWriter pw= null;
        try {
            pw = new PrintWriter(socket.getOutputStream());
            //http请求固定格式
            pw.println("HTTP/1.1 200 OK");
            pw.println("Content-Type: text/html;charset=UTF-8");
            pw.println();
            //用于接收浏览器的请求
            Request request = new Request();
            //用于返回信息到浏览器
            Response response=new Response(pw);
            //读取请求信息
            InputStream inputStream = socket.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
            while (true){
                //浏览器发送的请求第一行是GET /index?name=zeb&pwd=pwd HTTP/1.1
                String msg=reader.readLine();
                //String.Trim()方法会去除字符串两端,不仅仅是空格字符,它总共能去除25种字符:
                //Trim删除的过程为从外到内,直到碰到一个非空白的字符为止,所以不管前后有多少个连续的空白字符都会被删除掉。
                if(null == msg || "".equals(msg.trim())){
                    break;
                }
                String[] msgs=msg.split(" ");
                //msg
                if(3 == msgs.length && "HTTP/1.1".equalsIgnoreCase(msgs[2])){
                    //get or post
                    request.setMethod(msgs[0]);
                    //获取参数name与pwd
                    //   /index?name=zhangsan&pwd=123456
                    //split方法工作原理是利用正则表达式,而在正则表达式中, "?"有特殊意思
                    //所以匹配"?"时要用转义字符"\",所以在正则表达式中匹配"?"的表达式是"\?", 而在Java中,\又是特殊字符, 所以还要进行转义, 所以最终变成"\\?"
                    //正则中匹配前面的子表达式零次或一次。
                    String[] attributesPath=msgs[1].split("\\?");
                    // /index
                    request.setPath(attributesPath[0]);
                    if(attributesPath.length>1) {
                        Map attributeMap=new HashMap<>();
                        String[] params=attributesPath[1].split("&");// name=zhangsan&pwd=123456
                        for(int i=0;i404");
        }else{
                if ("GET".equalsIgnoreCase(request.getMethod())){
                    httpServlet.doGet(request,response);
                }else if("POST".equalsIgnoreCase(request.getMethod())){
                    httpServlet.doPost(request,response);
                }
            }
    }catch (Exception e) {
            response.write("

500

"+ Arrays.toString(e.getStackTrace())); e.printStackTrace(); } } }
package com.yy.tomcat;
import java.util.Map;
//模拟HttpServletRequest
public class Request {
    private String path;
    private String method;
    private Map map;
    //省去了get和set方法
public HttpServlet  initServlet(){
    return ServletContainer.getHttpServlet(path);
}
    public String getParameter(String name)
    {
        String parameter=map.get(name);
        return parameter;
    }
}
package com.yy.tomcat;
import java.io.PrintWriter;
//模拟HttpServletResponse
public class Response {
    private PrintWriter writer;
    public Response(PrintWriter writer) {
        this.writer = writer;
    }
    public void write(String msg){
        writer.write(msg);
        writer.flush();
    }
}

package com.yy.tomcat;
import java.io.IOException;
//模拟HttpServlet,自己的写的Servlet类都要继承这个抽象类
public abstract class HttpServlet {
    public void doGet(Request request,Response response) throws IOException {
this.service(request,response);
    }
    public void doPost(Request request,Response response) throws IOException {
        this.service(request,response);
    }
    public void service(Request request,Response response) throws IOException {
        if("GET".equalsIgnoreCase(request.getMethod())){
doGet(request,response);
        }else{
            doPost(request,response);
        }
    }
}
package com.yy.tomcat;
import java.io.IOException;
//自己写的servlet
public class MyServlet extends HttpServlet {
    @Override
    public void doGet(Request request, Response response) throws IOException {
response.write("

Servlet GET response!

"+ "name: "+request.getParameter("name")+" , pwd: "+request.getParameter("pwd")); } @Override public void doPost(Request request, Response response) throws IOException { response.write("

Servlet POST response!

"+ "name: "+request.getParameter("name")+" , pwd: "+request.getParameter("pwd")); } }
package com.yy.tomcat.model;
//解析web.xml用到的实体
public class Servlet {
private String name;
private String clazz;
//省去了set和get方法,本来是class,因为class是关键字用clazz代替
}
package com.yy.tomcat.model;
//解析web.xml用到的实体
public class ServletMapping {
    private String name;
    private String url;
}
package com.yy.tomcat.util;
import java.io.File;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import com.yy.tomcat.model.Servlet;
import com.yy.tomcat.model.ServletMapping;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
//解析web.xml 了解即可
public class XMLUtil {
    public static Map> parseWebXML() throws Exception{
        Map> result=new HashMap>();
        DocumentBuilderFactory dbf=DocumentBuilderFactory.newInstance();
        DocumentBuilder db=dbf.newDocumentBuilder();        
        
        InputStream in=XMLUtil.class.getClassLoader().getResourceAsStream("web.xml");
        Document document=db.parse(in);
        Element root=document.getDocumentElement();
        System.out.println("rootName: "+root.getTagName());//web-app
        
        NodeList xmlNodes=root.getChildNodes();     
        for(int i=0;i servletMaps=null;
                    if(result.containsKey(0)) {
                        servletMaps=result.get(0);
                    }else {
                        servletMaps=new HashMap();
                    }                   
                    NodeList childNodes=config.getChildNodes();
                    Servlet servlet=new Servlet();
                    for(int j=0;j servletMappingMaps=null;
                    if(result.containsKey(1)) {
                        servletMappingMaps=result.get(1);
                    }else {
                        servletMappingMaps=new HashMap();
                    }
                    
                    NodeList childNodes=config.getChildNodes();
                    ServletMapping servletMapping=new ServletMapping();
                    
                    for(int j=0;j


    
    
        myServlet
        com.yy.tomcat.MyServlet
    
    
    
        myServlet
        
        /index
    

package com.yy.tomcat;
import com.yy.tomcat.model.Servlet;
import com.yy.tomcat.model.ServletMapping;
import com.yy.tomcat.util.XMLUtil;
import java.util.HashMap;
import java.util.Map;
//Servlet容器,解析web.xml根据url信息拿到响应的servlet
public class ServletContainer {
    //3个servlet容器
    private static Map servletMaps=new HashMap<>();
    private static Map servletMappingMaps=new HashMap<>();
    private static Map servletContainer=new HashMap<>();
    //通过静态块去加载配置文件中映射对应的model实体
    static{
        try {
            Map> maps = XMLUtil.parseWebXML();
            if(null != maps&&2 ==maps.size()){
                servletMaps=maps.get(0);
                servletMappingMaps=maps.get(1);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
//通过工具类加载,从servlet容器中获取对应的servlet
    public static HttpServlet getHttpServlet(String path){
//访问跟路径/时修改为index路径
        if(null == path ||"".equals(path.trim()) || "/".equals(path)){
path="/index";
        }
        //如果servletContainer容器中有就直接取走
        if (servletContainer.containsKey(path)){
            return servletContainer.get(path);
        }
        if (!servletMappingMaps.containsKey(path)){
            return null;
        }
        ServletMapping servletMapping= (ServletMapping) servletMappingMaps.get(path);
        String name=servletMapping.getName();
        if(!servletMaps.containsKey(name)) {
            return null;
        }
        Servlet servlet=(Servlet) servletMaps.get(name);
        String clazz=servlet.getClazz();
        if(null==clazz || "".equals(clazz.trim())) {
            return null;
        }
        HttpServlet httpServlet=null;
        try {
            httpServlet=(HttpServlet) Class.forName(clazz).newInstance();//通过反射获取servlet实体
            servletContainer.put(path, httpServlet);//添加到servlet容器中
        } catch (Exception e) {
            e.printStackTrace();
        }
        return httpServlet;
    }
}

你可能感兴趣的:(Tomcat源码分析)