查看: 108|回复: 0

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

[复制链接]

3

主题

5

帖子

11

积分

新手上路

Rank: 1

积分
11
发表于 2023-6-5 12:13:06 | 显示全部楼层 |阅读模式

1 项目整体结构的设计

为了让新同学深入的理解tomcat的运行原理,动手实现一个简单的tomcat服务器。
文章比较长,而且需要跟着动手练习,才能有所收获。

思路模型图:






编辑
项目结构图:






编辑
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文件






编辑
/* * 读取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="\r\n";  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="\r\n";  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中使用多线程处理
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

快速回复 返回顶部 返回列表