1 项目整体结构的设计

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

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

思路模型图:

项目结构图:




java 实体类 对象转为list java实体类转map的工具包_java 实体类 对象转为list


java 实体类 对象转为list java实体类转map的工具包_java_02


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

logincom.bjsxt.servlet.LoginServletlogin/login/login2

2 Entity实体类的处理

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

3 Mapping映射类的处理

/**
 * 用于处理servlet别名和对应映射路径的实体类
 * 
 * login
 * /login
 * /login2
 * 
 */
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文件

/*
* 读取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 entityList;
 * servlet-name作为键
 * servlet-class作为值
 */private Map<String,String> servlet;/*
 List 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页面

="http://127.0.0.1:8888/login" method ="post">

用户名:="text" name="username" />

密码:="password" name="password" />

登录身份:

="checkbox" name="role" value="manager" />管理人员

="checkbox" name="role" value="clerk" />普通员工

="checkbox" name="role" value="visiter" />游客

="submit" />

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="";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="";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;}@Overridepublic 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();}}
}