摘要:项目结构项目结构如下实现细节创建对象首先创建自定义的请求类,其中定义与两个属性,表示请求的以及请求的方式。其构造函数需要传入一个输入流,该输入流通过客户端的套接字对象得到。创建服务端套接字,并绑定某个端口。
缘起
用了那么久tomcat,突然觉得自己对它或许并没有想象中的那么熟悉,所以趁着放假我研究了一下这只小猫咪,实现了自己的小tomcat,写出这篇文章同大家一起分享!
照例附上github链接。
项目结构如下:
首先创建自定义的请求类,其中定义url与method两个属性,表示请求的url以及请求的方式。
其构造函数需要传入一个输入流,该输入流通过客户端的套接字对象得到。
输入流中的内容为浏览器传入的http请求头,格式如下:
GET /student HTTP/1.1 Host: localhost:8080 Connection: keep-alive Pragma: no-cache Cache-Control: no-cache Upgrade-Insecure-Requests: 1 User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8 Accept-Encoding: gzip, deflate, br Accept-Language: zh-CN,zh;q=0.9 Cookie: Hm_lvt_eaa22075ffedfde4dc734cdbc709273d=1549006558; _ga=GA1.1.777543965.1549006558
通过对以上内容的分割与截取,我们可以得到该请求的url以及请求的方式。
package tomcat.dome; import java.io.IOException; import java.io.InputStream; //实现自己的请求类 public class MyRequest { //请求的url private String url; //请求的方法类型 private String method; //构造函数 传入一个输入流 public MyRequest(InputStream inputStream) throws IOException { //用于存放http请求内容的容器 StringBuilder httpRequest=new StringBuilder(); //用于从输入流中读取数据的字节数组 byte[]httpRequestByte=new byte[1024]; int length=0; //将输入流中的内容读到字节数组中,并且对长度进行判断 if((length=inputStream.read(httpRequestByte))>0) { //证明输入流中有内容,则将字节数组添加到容器中 httpRequest.append(new String(httpRequestByte,0,length)); } //将容器中的内容打印出来 System.out.println("httpRequest = [ "+httpRequest+" ]"); //从httpRequest中获取url,method存储到myRequest中 String httpHead=httpRequest.toString().split(" ")[0]; url=httpHead.split("s")[1]; method=httpHead.split("s")[0]; System.out.println("MyRequests = [ "+this+" ]"); } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getMethod() { return method; } public void setMethod(String method) { this.method = method; } }
创建自定义的响应类,构造函数需要传入由客户端的套接字获取的输出流。
定义其write方法,像浏览器写出一个响应头,并且包含我们要写出的内容content。
package tomcat.dome; import java.io.IOException; import java.io.OutputStream; //实现自己的响应类 public class MyResponse { //定义输出流 private OutputStream outputStream; //构造函数 传入输出流 public MyResponse(OutputStream outputStream) { this.outputStream=outputStream; } //创建写出方法 public void write(String content)throws IOException{ //用来存放要写出数据的容器 StringBuffer stringBuffer=new StringBuffer(); stringBuffer.append("HTTP/1.1 200 OK ") .append("Content-type:text/html ") .append(" ") .append("Hello World ") .append(content) .append(""); //转换成字节数组 并进行写出 outputStream.write(stringBuffer.toString().getBytes()); //System.out.println("sss"); outputStream.close(); } }
由于我们自己写一个Servlet的时候需要继承HttpServlet,因此在这里首先定义了一个抽象类——MyServlet对象。
在其中定义了两个需要子类实现的抽象方法doGet和doSet。
并且创建了一个service方法,通过对传入的request对象的请求方式进行判断,确定调用的是doGet方法或是doPost方法。
package tomcat.dome; //写一个抽象类作为servlet的父类 public abstract class MyServlet { //需要子类实现的抽象方法 protected abstract void doGet(MyRequest request,MyResponse response); protected abstract void doPost(MyRequest request,MyResponse response); //父类自己的方法 //父类的service方法对传入的request以及response //的方法类型进行判断,由此调用doGet或doPost方法 public void service(MyRequest request,MyResponse response) throws NoSuchMethodException { if(request.getMethod().equalsIgnoreCase("POST")) { doPost(request, response); }else if(request.getMethod().equalsIgnoreCase("GET")) { doGet(request, response); }else { throw new NoSuchMethodException("not support"); } } }
这里我创建了两个业务相关类:StudentServlet和TeacherServlet。
package tomcat.dome; import java.io.IOException; //实现自己业务相关的Servlet public class StudentServlet extends MyServlet{ @Override protected void doGet(MyRequest request, MyResponse response) { //利用response中的输出流 写出内容 try { //System.out.println("!!!!!!!!!!!!!!!!!!"); response.write("I am a student."); //System.out.println("9999999999999999"); }catch(IOException e) { e.printStackTrace(); } } @Override protected void doPost(MyRequest request, MyResponse response) { //利用response中的输出流 写出内容 try { response.write("I am a student."); }catch(IOException e) { e.printStackTrace(); } } }
package tomcat.dome; import java.io.IOException; //实现自己业务相关的Servlet public class TeacherServlet extends MyServlet{ @Override protected void doGet(MyRequest request, MyResponse response) { //利用response中的输出流 写出内容 try { response.write("I am a teacher."); }catch(IOException e) { e.printStackTrace(); } } @Override protected void doPost(MyRequest request, MyResponse response) { //利用response中的输出流 写出内容 try { response.write("I am a teacher."); }catch(IOException e) { e.printStackTrace(); } } }
该结构实现的是请求的url与具体的Servlet之间的关系映射。
package tomcat.dome; //请求url与项目中的servlet的映射关系 public class ServletMapping { //servlet的名字 private String servletName; //请求的url private String url; //servlet类 private String clazz; public String getServletName() { return servletName; } public void setServletName(String servletName) { this.servletName = servletName; } public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } public String getClazz() { return clazz; } public void setClazz(String clazz) { this.clazz = clazz; } public ServletMapping(String servletName, String url, String clazz) { super(); this.servletName = servletName; this.url = url; this.clazz = clazz; } }
配置类中定义了一个列表,里面存储着项目中的映射关系。
package tomcat.dome; import java.util.ArrayList; import java.util.List; //创建一个存储有请求路径与servlet的对应关系的 映射关系配置类 public class ServletMappingConfig { //使用一个list类型 里面存储的是映射关系类Mapping public static ListservletMappings=new ArrayList<>(16); //向其中添加映射关系 static { servletMappings.add(new ServletMapping("student","/student", "tomcat.dome.StudentServlet")); servletMappings.add(new ServletMapping("teacher","/teacher", "tomcat.dome.TeacherServlet")); } }
在服务端MyTomcat中主要做了如下几件事情:
1)初始化请求的映射关系。
2)创建服务端套接字,并绑定某个端口。
3)进入循环,用户接受客户端的链接。
4)通过客户端套接字创建request与response对象。
5)根据request对象的请求方式调用相应的方法。
6)启动MyTomcat!
package tomcat.dome; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.ServerSocket; import java.net.Socket; import java.util.HashMap; import java.util.Map; //Tomcat服务器类 编写对请求做分发处理的相关逻辑 public class MyTomcat { //端口号 private int port=8080; //用于存放请求路径与对应的servlet类的请求映射关系的map //相应的信息从配置类中获取 private MapurlServletMap=new HashMap<>(16); //构造方法 public MyTomcat(int port) { this.port=port; } //tomcat服务器的启动方法 public void start() { //初始化请求映射关系 initServletMapping(); //服务端的套接字 ServerSocket serverSocket=null; try { //创建绑定到某个端口的服务端套接字 serverSocket=new ServerSocket(port); System.out.println("MyTomcat begin start..."); //循环 用于接收客户端 while(true) { //接收到的客户端的套接字 Socket socket=serverSocket.accept(); //获取客户端的输入输出流 InputStream inputStream=socket.getInputStream(); OutputStream outputStream=socket.getOutputStream(); //通过输入输出流创建请求与响应对象 MyRequest request=new MyRequest(inputStream); MyResponse response=new MyResponse(outputStream); //根据请求对象的method分发请求 调用相应的方法 dispatch(request, response); //关闭客户端套接字 socket.close(); } } catch (IOException e) { e.printStackTrace(); } } //初始化请求映射关系,相关信息从配置类中获取 private void initServletMapping() { for(ServletMapping servletMapping:ServletMappingConfig.servletMappings) { urlServletMap.put(servletMapping.getUrl(), servletMapping.getClazz()); } } //通过当前的request以及response对象分发请求 private void dispatch(MyRequest request,MyResponse response) { //根据请求的url获取对应的servlet类的string String clazz=urlServletMap.get(request.getUrl()); //System.out.println("====="+clazz); try { //通过类的string将其转化为对象 Class servletClass=Class.forName("tomcat.dome.StudentServlet"); //实例化一个对象 MyServlet myServlet=(MyServlet)servletClass.newInstance(); //调用父类方法,根据request的method对调用方法进行判断 //完成对myServlet中doGet与doPost方法的调用 myServlet.service(request, response); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } //main方法 直接启动tomcat服务器 public static void main(String[] args) { new MyTomcat(8080).start(); } }
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/73638.html
摘要:算是心血来潮吧,想看看的源码,顺便学习一下。这里就简单的做点记录如何用跟踪查看源码。当然这里也提供书籍的源码,可以下载下来对照书看。附上一张运行成功的结果图 算是心血来潮吧,想看看 Tomcat 的源码,顺便学习一下。 这里就简单的做点记录--如何用 IDEA 跟踪查看 Tomcat 源码。 同时,这里也推荐下 how-tomcat-works 这本书,虽然书中讲的是 tomc...
摘要:接下来我们要配置这个的端口,这样他们才能运行时端口号不冲突。问题指明不同的端口号访问也太蠢了吧的确很蠢,所以我们要慢慢过渡学习。接下来我们学习用来进行反向代理。阿里云的部分有一些配置的具体过程。 一、在linux上部署运行多个tomcat 1、以前的我们 虽然说是在linux上,但是windows上也是同样的道理,只不过我们服务器都是选用linux罢了。 原先,自己有多个项目需要部署在...
阅读 2564·2023-04-25 18:13
阅读 772·2021-11-22 12:10
阅读 2973·2021-11-22 11:57
阅读 2138·2021-11-19 11:26
阅读 2165·2021-09-22 15:40
阅读 1461·2021-09-03 10:28
阅读 2706·2019-08-30 15:53
阅读 1952·2019-08-30 15:44