【web server】手写tomcat

前言

上一篇讲了tomcat的理论核心,tomcat是由一层层容器套成的跟俄罗斯套娃似的,服务端有server,service,engine,context,servlet等,但是web端没有仔细讲。一个url怎么进入程序,并是怎样被解析找到对应的类的方法的?下面我们动手写一个。

4步走

1.获取配置文件中的url和对应的类名,并创建servletMapping文件
2.死循环等待用户请求
2.封装serverSocket的inputstream,outputstream为HttpRequest,HttpResponse
4.获取httpRequest的访问方式和方法,根据mapping动态调用对应的servlet的doGet或doPost

4个核心概念

1.serverSocket inputStream outputStream
2.HttpRequest HttpResponse
3.servletMapping
4.servlet doGet doPost

代码实现

tomcat类:

public class HLPTomcat {
    private int port = 8084;
    private ServerSocket serverSocket;
    private Map<String,HLPServlet> servletMapping = new HashMap<String,HLPServlet>();
    private Properties webXml = new Properties();

    private void init(){
        //加载web.xml,初始化servletmapping对象
        try{
//            String basePath = this.getClass().getResource("conf").getPath();
            String basePath =Thread.currentThread().getContextClassLoader().getResource("conf/tomcat.properties").getPath();
            FileInputStream fis = new FileInputStream(basePath);
            webXml.load(fis);
        }catch (Exception e){
            e.printStackTrace();
        }

        for(Object k : webXml.keySet()){
            String key = k.toString();
            if(key.endsWith(".url")){
                String servletName = key.replaceAll("\\.url$","");
                String url = webXml.getProperty(key);
                String className = webXml.getProperty(servletName + ".className");
                HLPServlet obj = null;
                try {
                    obj = (HLPServlet) Class.forName(className).newInstance();
                } catch (InstantiationException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
                servletMapping.put(url,obj);

            }
        }

    }

    //3.把serverSocket的inputstream, outputstream 封装成request, response对象
    private void process(Socket client)throws Exception
    {
        InputStream ins = client.getInputStream();
        OutputStream outs = client.getOutputStream();
        HLPRequest hlpRequest = new HLPRequest(ins);
        HLPResponse hlpResponse = new HLPResponse(outs);
        //4.动态调用doget,dopost
        String url = hlpRequest.getUrl();
        if(servletMapping.containsKey(url)){
            servletMapping.get(url).service(hlpRequest,hlpResponse);

        }else{
            hlpResponse.write("404-Not Found");
        }
        outs.flush();
        outs.close();
        client.close();

    }

    public void start() {
//        1.初始化
        init();

        try {
            serverSocket = new ServerSocket(this.port);
            System.out.println("tomcat is started and the port is " + this.port);
//            Socket socket = serverSocket.accept();
//            System.out.println(socket);
//2.死循环等待用户请求
            while(1==1){
                Socket client = serverSocket.accept();
                process(client);
//                OutputStream op = client.getOutputStream();
//                op.write("Hello".getBytes());
//                op.close();
//                client.close();
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        new HLPTomcat().start();
    }
}

httpservlet类:

public abstract class HLPServlet {
    public void service(HLPRequest request, HLPResponse response)throws Exception{
        if("Get".equalsIgnoreCase(request.getMethod())){
            doGet(request,response);
        }else{
            doPost(request,response);
        }
    }

    public abstract void doGet (HLPRequest request, HLPResponse response);

    public abstract void doPost (HLPRequest request, HLPResponse response);


}

httpRequest类:

public class HLPRequest {
    String method;
    String url;

    public HLPRequest(InputStream ins){
        try{
            byte[] buff = new byte[1024];
            String content = "";
            int len = 0;
            if ((len = ins.read(buff)) >0){
                content = new String(buff,0,len);
                //System.out.println(content);
            }
        String line = content.split("\\n")[0];
        String[] arr = line.split("\\s");
        this.method = arr[0];
        this.url = arr[1].split("\\?")[0];
        }catch (Exception e){
            e.printStackTrace();
        }

    }

    public String getUrl(){
        return this.url;
    }
    public String getMethod(){
        return this.method;
    }

    public Map<String,String> getParameter(){
        return null;
    }

}

httpResponse类:

public class HLPResponse {
    private  OutputStream outs;
    public HLPResponse(OutputStream outs){
        this.outs=outs;
    }
    public void write(String outString)throws Exception{
        outs.write(outString.getBytes());
    }
}

firstServlet类:

public class FirstServlet extends HLPServlet {
    @Override
    public void doGet(HLPRequest request, HLPResponse response) {
        try {
            response.write("this is first servlet");
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    @Override
    public void doPost(HLPRequest request, HLPResponse response) {
        try {
            response.write("this is first servlet");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

secondServlet类:

public class SecondServlet extends HLPServlet {
    @Override
    public void doGet(HLPRequest request, HLPResponse response) {
        try {
            response.write("this is second servlet");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void doPost(HLPRequest request, HLPResponse response) {
        try {
            response.write("this is second servlet");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

代码中的小感悟

1.如何把本地文件加载到代码中

  • 获取本地文件的路径
String basePath =Thread.currentThread().getContextClassLoader().getResource("conf/tomcat.properties").getPath();
  • 生成inputstream
 FileInputStream fis = new FileInputStream(basePath);
  • 把inputstream读到properties文件中
private Properties webXml = new Properties();
webXml.load(fis);

2.mapping是一个hashmap, key是string, value是一个servlet实例
如何应用反射创建的类实例?

obj = (HLPServlet) Class.forName(className).newInstance();

反射

1.类加载
–1.加载:加载代码到方法区;在堆里创建一个类文件的对象
–2.连接
~1.验证
~2.准备 为静态变量,静态方法初始化
~3.解析 把a=b 变成1
–3.初始化 ~1.开辟空间
~2.赋初值
2.类初始化时机–1.实例类–2实例自类–3.调用静态成员变量–4.调用静态成员方法–5.反射强制创建某个类或接口的java.lang.class对象
3.类加载器
–1.bootstrap classLoader 加载核心类库 rt.jar
–2.extansion classLoader 扩展加载器
–3.system classLoader 系统加载器
4.配置文件使类不需要更改
5.反射:运行时随便一个class文件,反射都可以解剖,拿到里面所有的属性和方法/动态获取一个类中的所有内容并运行

{
 FileReader f = new FileReader("dd");
        Properties p = new Properties();
        p.load(f);
        f.close();
        String className = p.getProperty("name");
        String method = p.getProperty("method");
        Class c  = Class.forName(className);
        Object o = c.newInstance();
        Method m = c.getMethod(method);
        m.invoke(o);
}

类文件对象的描述文件是java.lang.Java6.获取一个类的class文件对象的三种方式:
–1.对象获取Person p = new Person();p.getClass();
–2.类名获取Person.class();
–3.class类的静态方法获取Class c = Class.forName(“className”);

总结

这里写图片描述

猜你喜欢

转载自blog.csdn.net/boniesunshine/article/details/81192828