模拟java_java入门--模拟实现一个tomcat服务器

1 项目整体结构的设计

为了让新同学深入的理解tomcat的运行原理,动手实现一个简单的tomcat服务器。

文章比较长,而且需要跟着动手练习,才能有所收获。

思路模型图:

b58dc26523d0bfe8ff8b823fcf2f07ae.png

项目结构图:

cfbd45869d8ba23695d7fa1af1dde924.png

web.xml映射文件中的内容,用于定义URL请求映射路径和java类之间的对应关系

<?xml version="1.0" encoding="UTF-8"?>
<web-app>
 <!--配置别名和对应的servletclass-->
 <servlet>
 <servlet-name>login</servlet-name>
 <sevrlet-class>com.bjsxt.servlet.LoginServlet</sevrlet-class>
 </servlet>
 <!--配置请求的映射路径对应的servletname-->
 <servlet-mapping>
 <servlet-name>login</servlet-name>
 <url-pattern>/login</url-pattern>
 <url-pattern>/login2</url-pattern>
 </servlet-mapping>
</web-app>

2 Entity实体类的处理

/**
 * 用于处理别名和对应servlet的实体类
 *  <sevlet>
 *       <sevlet-name>login</sevlet-name>
 *       <sevlet-class>com.bjsxt.servlet.LoginServlet</sevlet-class>
 *   </sevlet>
 */
public class Entity {
 private String name;// servlet-name
 private String clazz;// servlet-class

3 Mapping映射类的处理

/**
 * 用于处理servlet别名和对应映射路径的实体类
 *     <sevlet-mapping>
 *         <sevlet-name>login</sevlet-name>
 *         <url-pattern>/login</url-pattern>
 *         <url-pattern>/login2</url-pattern>
 *     </sevlet-mapping>
 */
public class Mapping {
 private String name;//servlet-name
 private List<String> urlPattern;//多个url-pattern
 
 public Mapping() {
 this.urlPattern =new ArrayList<String>();
    }
 public Mapping(String name, List<String> urlPattern) {
 this();
 this.name = name;
 this.urlPattern = urlPattern;
    }

4 DOM4j解析xml映射文件

导入DOM4J jar包,帮助我们解析web.xml文件

625060498f06f354d39562bbd86e00ea.png
/*
* 读取web.xml配置文件
* 配置文件中的信息封装进多个 entity对象和多个mapping对象中
*
* */
public class WebDom4j {
 private List<Entity> entityList;
 private List<Mapping> mappingList;

 public WebDom4j() {
 entityList=new ArrayList<Entity>();
 mappingList=new ArrayList<Mapping>();
    }

 public WebDom4j(List<Entity> entityList, List<Mapping> mappingList) {
 this();
 this.entityList = entityList;
 this.mappingList = mappingList;
    }

 public List<Entity> getEntityList() {
 return entityList;
    }

 public void setEntityList(List<Entity> entityList) {
 this.entityList = entityList;
    }

 public List<Mapping> getMappingList() {
 return mappingList;
    }

 public void setMappingList(List<Mapping> mappingList) {
 this.mappingList = mappingList;
    }
 // 读取配置文件,生成一个Document对象的方法
 public Document getDocument()  {
        SAXReader reader =new SAXReader();
        File webXml=new File("myserver/WEB-INF/web.xml");
 try {
 return  reader.read(webXml);
        } catch (DocumentException e) {
            e.printStackTrace();
        }
 return null;
    }
 // 将Document对象解析成Entity对象和Mapping对象 并放入 entityList 和 mappingList中
 public void parseDocument(Document document){
 // 获取根标签
 Element wepApp = document.getRootElement();
 // 获取多个servlet标签
 Iterator<Element> servletElements = wepApp.elementIterator("servlet");
 while(servletElements.hasNext()){
            Element servletElement = servletElements.next();
            Element servletName = servletElement.element("servlet-name");
            Element servletClass = servletElement.element("servlet-class");
            Entity entity=new Entity(servletName.getText(),servletClass.getText());
 entityList.add(entity);
        }
 // 获取多个servlet-mapping标签
 Iterator<Element> servletMappings = wepApp.elementIterator("servlet-mapping");
 while(servletMappings.hasNext()){
            Mapping mapping =new Mapping();
            Element servletMapping = servletMappings.next();
 //取出servlet-name
 Element servletName = servletMapping.element("servlet-name");
            mapping.setName(servletName.getText());
            Iterator<Element> urlPatterns = servletMapping.elementIterator("url-pattern");
            List<String> patterns=new ArrayList<String>();
 while(urlPatterns.hasNext()){
                Element urlPattern = urlPatterns.next();
                patterns.add(urlPattern.getText());
            }
            mapping.setUrlPattern(patterns);
 mappingList.add(mapping);
        }
    }
}

5处理ServletContext类

全局容器,目前用于存储映射关系

import java.util.HashMap;
import java.util.Map;

/*
* 全局容器类  存储整个项目的信息
* 暂时我们 需要他存储 全局映射关系信息
* Map mapping 存储映射路径和别名   Map servlet  别名和servlet类的全路径名
* url路径  >>>>> servlet别名      servlet别名 >>>> servlet类的全路径名
* /login         login           login          com.bjsxt.servlet.LoginServlet
* /login2        login
* */
public class ServletContext {
 /**
     * List<Entity> entityList;
     * servlet-name作为键
 * servlet-class作为值
 */
 private Map<String,String> servlet;
 /*
    List<Mapping> mappingList;
    * url-pattern 作为键
    * servlet-name作为值
    * */
 private Map<String,String> mapping;

 public ServletContext() {
 servlet=new HashMap<String,String>();
 mapping=new HashMap<String,String>();
    }

 public ServletContext(Map<String, String> servlet, Map<String, String> mapping) {
 this();
 this.servlet = servlet;
 this.mapping = mapping;
    }

6 编写WebApp类

运行时初始化程序

根据不同的url使用反射创建不同的servlet对象

package com.bjsxt.server;

import com.bjsxt.servlet.Servlet;
import org.dom4j.Document;

import java.util.List;
import java.util.Map;

public class WebApp {
 // 将WebDom4j中的信息转化到ServletContext类中
 private static ServletContext context;
 static{
 context=new ServletContext();
        Map<String, String> servlet = context.getServlet();
        Map<String, String> mapping = context.getMapping();
        WebDom4j dom4j=new WebDom4j();
        Document document = dom4j.getDocument();
        dom4j.parseDocument(document);
        List<Entity> entityList = dom4j.getEntityList();
 for(Entity entity:entityList){
            String name = entity.getName();
            String clazz = entity.getClazz();
            servlet.put(name,clazz);
        }
        List<Mapping> mappingList = dom4j.getMappingList();
 for(Mapping mappingx:mappingList){
            String name = mappingx.getName();
            List<String> urlPatterns = mappingx.getUrlPattern();
 for(String urlPattern:urlPatterns){
                mapping.put(urlPattern,name);
            }
        }
    }

 // 根据映射路径返回对应的servlet对象
 public static Servlet getServlet(String urlPattern){// /login  >>> com.bjsxt.servlet.LoginServlet
 if(null == urlPattern || "".equals(urlPattern)){
 return null;
        }
        Map<String, String> mappging = context.getMapping();
        String servletName = mappging.get(urlPattern);
 if(null == servletName){
 return null;
        }
        Map<String, String> servlet = context.getServlet();
        String servletClassName = servlet.get(servletName);
 if(null == servletClassName){
 return null;
        }
 try {
            Class servletClazz = Class.forName(servletClassName);
 return (Servlet) servletClazz.newInstance();
        } catch (Exception e) {
            e.printStackTrace();
        }
 return null;
    }
}

7 准备提交信息的HTML页面

<form action="http://127.0.0.1:8888/login" method="post">
 用户名:<input type="text" name="username" /><br/>
 密码:<input type="password" name="password" /><br />
 登录身份:
 <input type="checkbox" name="role" value="manager" />管理人员
 <input type="checkbox" name="role" value="clerk" />普通员工
 <input type="checkbox" name="role" value="visiter" />游客
 <br />
 <input type="submit" />
</form>

8 封装请求对象

将请求中的信息放入一个Request对象中,便于我们直接从Request对象上获取信息

package com.bjsxt.server;



import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.util.*;

public class Request {
 private InputStream is;
 private String requestInfo;
 private String method;// post get
 private String url;//
    //用于存储参数的Map集合
 //username=zhangsan&password=qwer&role=manager&role=clerk
    /*
    *  key         value
    * username     [zhangsan]
    * password     [qwer]
    * role         [manager,clerk]
    *
    * */
 private Map<String, List<String>> paramaterValues;
 private static final String CRLF="rn";
 private static final String BLANK=" ";

 public String getUrl() {
 return url;
    }

 public Request(){
 requestInfo="";
 method="";
 url="";
 paramaterValues=new HashMap<String,List<String>>();
    }


 public Request(InputStream is){
 this();
 this.is=is;
        paraseRequeseInfo();
    }
 public void paraseRequeseInfo(){
 // 获取请求信息
 byte[] buf=new byte[10240];
 int index = 0;
 try {
            index = is.read(buf);
        } catch (IOException e) {
            e.printStackTrace();
        }
 requestInfo=new String (buf,0,index);
 // 取出请求行   请求方式  url地址  请求的协议
 String requestLine=requestInfo.substring(0,requestInfo.indexOf(CRLF)).trim();
        String[] requestLines = requestLine.split(" ");
 method=requestLines[0];
        String paramaterLine="";
 if(method.equalsIgnoreCase("get")){// 请求方式是get
 if(requestLines[1].contains("?")){
 url=requestLines[1].substring(0,requestLines[1].indexOf("?"));
                String[] urls=requestLines[1].split("?");
                paramaterLine=urls[1].trim();
            }else{
 url=requestLines[1];
            }
        }else{//请求方式是Post
 url=requestLines[1];
            paramaterLine=requestInfo.substring(requestInfo.lastIndexOf(CRLF)+1).trim();
        }
        parseParamater(paramaterLine);
    }
 public void parseParamater(String paramaterLine){
 //username=asdf&password=qwer&role=manager&role=clerk

 System.out.println("paramaterLine:"+paramaterLine);
 if("".equalsIgnoreCase(paramaterLine)){
 return;
        }
        String[] lines = paramaterLine.split("&");
 /*
        [username,asdf]
        [password,qwer]
        [role,manager]
        [role,clerk]
        */
        /*
         *  key         value
         * username     [zhangsan]
         * password     [qwer]
         * role         [manager,clerk]
         *
         * */
 for(String line:lines){
            String[] keyValue = line.split("=");
 if(keyValue.length==1){// key=
 keyValue=Arrays.copyOf(keyValue,2);
                keyValue[1]=null;
            }
            String key=keyValue[0];
            String value=keyValue[1]==null?null:decode(keyValue[1],"UTF-8");
 if(paramaterValues.containsKey(key)){//
 List<String> values = paramaterValues.get(key);
                values.add(value);
            }else{
                List<String> values=new ArrayList<String>();
                values.add(value);
 paramaterValues.put(key,values);
            }
        }
        System.out.println(paramaterValues);
    }
 public String decode(String value,String charset){
 // %E5%BC%A0%E4%B8%89     UTF-8
 try {
 return URLDecoder.decode(value,charset);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
 return null;
    }

 // 根据键向外界返回参数的方法
 public String getParamater(String name){
 if(paramaterValues.containsKey(name)){
            List<String> list = paramaterValues.get(name);
 if(null == list || list.size()==0){
 return null;
            }else{
 return list.get(0);
            }
        }
 return null;
    }


 public String[] getParamaterValues(String name){
 if(paramaterValues.containsKey(name)){
            List<String> list = paramaterValues.get(name);
 if(null == list || list.size()==0){
 return null;
            }else{
                String[] values = list.toArray(new String[0]);
 return values;
            }
        }
 return null;
    }
}

9封装响应对象

package com.bjsxt.server;

import java.io.*;

public class Response {
 // 字符输出流
 private BufferedWriter bw;
 // 响应行
 private StringBuilder responseLine;
 // 响应头
 private StringBuilder responseHeaders;
 // 响应数据实体
 private StringBuilder content;
 // 响应数据的长度
 private int length;
 private static final String CRLF="rn";
 private static final String BLANK=" ";

 public Response() {
 responseLine=new StringBuilder();
 responseHeaders=new StringBuilder();
 content=new StringBuilder();
 length=0;
    }
 public Response(OutputStream os){
 this();
 bw =new BufferedWriter(new OutputStreamWriter(os));
    }

 // 处理响应行的方法
 public void createResponseLine(int code){
 //HTTP/1.1 200 OK
 responseLine.append("HTTP/1.1").append(BLANK).append(code).append(BLANK);
 switch (code){
 case 200:
 responseLine.append("OK");
 break;
 case 404:
 responseLine.append("NOTFOUND");
 break;
 case 500:
 responseLine.append("SERVEREXCEPTION");
 break;
 default:
 responseLine.append("Other");
 break;
        }
 responseLine.append(CRLF);
    }

 // 生成响应头的方法
 public void createResponseHeaders(){
 responseHeaders.append("Content-Type: text/html;charset=UTF-8").append(CRLF);
 responseHeaders.append("Content-Length: ").append(length).append(CRLF).append(CRLF);
    }

 // 生成响应内容的方法
 public void write(String info){
 content.append(info);
 try {
 length+=info.getBytes("UTF-8").length;
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }

 // 将内容响应给浏览器的方法
 public void responseToBrowser(int code){
        createResponseLine(code);
        createResponseHeaders();
        StringBuilder info=new StringBuilder();
        info.append(responseLine).append(responseHeaders).append(content);
 try {
 bw.write(info.toString());
 bw.flush();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }


}

创建server类,用于启动服务,接受请求

package com.bjsxt.server;

import com.bjsxt.servlet.Servlet;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.ServerSocket;
import java.net.Socket;

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

 private ServerSocket serverSocket;
 // 启动服务,接受请求
 public void start(){
 try {

 serverSocket=new ServerSocket(8888);
            System.out.println("服务已经启动,准备接受请求");
            Socket socket = serverSocket.accept();
            InputStream inputStream = socket.getInputStream();
            OutputStream outputStream = socket.getOutputStream();
 // 将请求信息封装进一个Request对象
 Request request =new Request(inputStream);
 // 封装一个Response响应对象
 Response response=new Response(outputStream);
 // 根据请求的url 实例化一个Servlet对象
 String url = request.getUrl();
            Servlet servlet = WebApp.getServlet(url);
 int code =200;
 if(null == servlet){
                code=404;
                response.write("未找到您要访问的资源");
            }else {
 // 调用servlet的service方法去处理请求
 try {
                    servlet.service(request,response);
                } catch (Exception e) {
                    code=500;
                    response.write(e.getMessage());
                }
            }
            response.responseToBrowser(code);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

10多线程处理

定义请求分发线程任务

import com.bjsxt.servlet.Servlet;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Socket;

public class RequestDispacher implements Runnable{
 private Socket socket;
 public RequestDispacher(Socket socket){
 this.socket=socket;
    }
 @Override
 public void run() {
        InputStream inputStream = null;
        OutputStream outputStream = null;
 try {
            inputStream = socket.getInputStream();
            outputStream = socket.getOutputStream();
        } catch (IOException e) {
            e.printStackTrace();
        }
 // 将请求信息封装进一个Request对象
 Request request =new Request(inputStream);
 // 封装一个Response响应对象
 Response response=new Response(outputStream);
 // 根据请求的url 实例化一个Servlet对象
 String url = request.getUrl();
        Servlet servlet = WebApp.getServlet(url);
 int code =200;
 if(null == servlet){
            code=404;
            response.write("未找到您要访问的资源");
        }else {
 // 调用servlet的service方法去处理请求
 try {
                servlet.service(request,response);
            } catch (Exception e) {
                code=500;
                response.write(e.getMessage());
            }
        }
        response.responseToBrowser(code);
 // 释放资源
 try {
            inputStream.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
 try {
 socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

在Server中使用多线程处理

import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;

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

 private ServerSocket serverSocket;
 // 启动服务,接受请求
 public void start(){
 try {

 serverSocket=new ServerSocket(8888);
            System.out.println("服务已经启动,准备接受请求");
 while(true){
                Socket socket = serverSocket.accept();
 new Thread(new RequestDispacher(socket)).start();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值