一个简单servlet容器

2.1 javax.servlet.Servlet接口

  • Servlet编程需要使用javax.servlet和javax.servlet.http两个包下的接口和类
  • 在所有的类中javax.servlet.Servlet接口是最重要的。所有的servlet程序都必须实现该接口或继承实现了该接口的类
  • tomcat8中该接口如下:
  •   package javax.servlet;
    import java.io.IOException;
    public interface Servlet {
    public void init(ServletConfig config) throws ServletException;
    public ServletConfig getServletConfig();
    public void service(ServletRequest req, ServletResponse res)
    throws ServletException, IOException;
    public String getServletInfo();
    public void destroy();
    }
  • 在Servlet接口中,init()、service()和destroy()方法是和servlet生命周期相关的方法。当实例化某个servlet类后,servlet容器就会调用其init()方法进行初始化。servlet容器只会调用该方法一次,调用后则可以执行service()方法了。
  • 在servlet接收任何请求之前,必须是经过初始化的。该方法可以进行覆盖,自定义初始化。
  • 当servlet的一个客户端请求到达后,servlet容器就调用相应的servlet的service方法,并将 javax.servlet.ServletRequest对象和javax.servlet.ServletResponse对象作为参数传入。
  • ServletRequest对象包含客户端的HTTP请求信息,ServletResponse对象则封装servlet的响应信息。
  • 在整个servlet周期内,service会被多次调用。
  • 在将servlet容器从服务中移除之前,servlet容器会调用servlet实例的destory方法。
  • 使用PrimitiveServlet来测试servlet容器应用程序
    •   import javax.servlet.*;
      import java.io.IOException;
      import java.io.PrintWriter; public class PrimitiveServlet implements Servlet { public void init(ServletConfig config) throws ServletException {
      System.out.println("init");
      } public void service(ServletRequest request, ServletResponse response)
      throws ServletException, IOException {
      System.out.println("from service");
      PrintWriter out = response.getWriter();
      out.println("Hello. Roses are red.");
      out.print("Violets are blue.");
      } public void destroy() {
      System.out.println("destroy");
      } public String getServletInfo() {
      return null;
      }
      public ServletConfig getServletConfig() {
      return null;
      }
      }

2.2应用程序1

  • 对一个Servlet的每个HTTP请求,一个功能齐全的servlet容器需要做到以下几点:

    • 当第一次调用某个servlet时,要载入该servlet类,并调用其init方法
    • 针对每一个request请求,创建一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例
    • 调用该servlet的service方法,将ServletRequest对象和ServletResponse对象作为参数传入
    • 当关闭servlet类时,调用destory方法
  • 接下来建立一个Servlet容器,功能如下:
    • 等待HTTP请求
    • 创建一个ServletRequest对象和一个ServletResponse对象
    • 若请求静态资源,则调用StaticResourceProcessor对象的process方法,传入上面的两个对象
    • 若请求servlet,则载入相应的servlet类,调用其service方法。
    • 类关系UML图如下:
    • 基于Java的Servlet容器实现,需要调用Java提供的有关接口,比如javax.servlet.ServletRequest和javax.servlet.ServletResponse。
    • 任何一个Servlet都需要实现Servlet接口或者继承实现该接口的类。
    • 具体的请求过程以及类型如图:
  • 代码示例:
    •   if (request.getUri().startsWith("/servlet/")) {
      ServletProcessor1 processor = new ServletProcessor1();
      processor.process(request, response);
      }
      else {
      StaticResourceProcessor processor = new StaticResourceProcessor();
      processor.process(request, response);
      }
    • 在上面代码中可以看出对于静态资源使用StaticResourceProcessor容器,对于动态Servlet资源使用ServletProcessor1容器

    •   package ex02.pyrmont;
      public class ServletProcessor1 {
      public void process(Request request, Response response) {
      String uri = request.getUri();
      String servletName = uri.substring(uri.lastIndexOf("/") + 1);
      URLClassLoader loader = null; try {
      // create a URLClassLoader
      URL[] urls = new URL[1];
      URLStreamHandler streamHandler = null;
      File classPath = new File(Constants.WEB_ROOT);
      // the forming of repository is taken from the createClassLoader method in
      // org.apache.catalina.startup.ClassLoaderFactory
      String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;
      // the code for forming the URL is taken from the addRepository method in
      // org.apache.catalina.loader.StandardClassLoader class.
      urls[0] = new URL(null, repository, streamHandler);
      loader = new URLClassLoader(urls);
      }
      catch (IOException e) {
      System.out.println(e.toString() );
      }
      Class myClass = null;
      try {
      myClass = loader.loadClass(servletName);
      }
      catch (ClassNotFoundException e) {
      System.out.println(e.toString());
      } Servlet servlet = null; try {
      servlet = (Servlet) myClass.newInstance();
      servlet.service((ServletRequest) request, (ServletResponse) response);
      }
      catch (Exception e) {
      System.out.println(e.toString());
      }
      catch (Throwable e) {
      System.out.println(e.toString());
      } }
      }
    • 上面代码通过newInstance创建了一个Servlet的实例,并调用了service方法,并传入参数request和response。但是该参数是向上转型的。

    • 这通常是不安全的,因为外部人员可以将其向下转型为Request的对象,就可以调用其方法.解决方法是创建Request和Response的外观类(外观类和原类实现同一个接口,在外观类中创建私有接口对象用原类进行赋值即可)。而在调用接口对象时使用外观类就不会导致原类的方法泄露

    • 示例如下:

    •   package ex02.pyrmont;
      public class RequestFacade implements ServletRequest { private ServletRequest request = null; public RequestFacade(Request request) {
      this.request = request;
      } /* implementation of the ServletRequest*/
      ...
      }
      package ex02.pyrmont; public class Request implements ServletRequest { private InputStream input;
      private String uri; public Request(InputStream input) {
      this.input = input;
      } public String getUri() {
      return uri;
      } private String parseUri(String requestString) {
      int index1, index2;
      index1 = requestString.indexOf(' ');
      if (index1 != -1) {
      index2 = requestString.indexOf(' ', index1 + 1);
      if (index2 > index1)
      return requestString.substring(index1 + 1, index2);
      }
      return null;
      } public void parse() {
      // Read a set of characters from the socket
      StringBuffer request = new StringBuffer(2048);
      int i;
      byte[] buffer = new byte[2048];
      try {
      i = input.read(buffer);
      }
      catch (IOException e) {
      e.printStackTrace();
      i = -1;
      }
      for (int j=0; j<i; j++) {
      request.append((char) buffer[j]);
      }
      System.out.print(request.toString());
      uri = parseUri(request.toString());
      } /* implementation of the ServletRequest*/
      }
    • 这样在上面service方法中使用RequestFacade类的实例向上转型就不会出现问题了。

    • 运行结果如下:



一个简单servlet容器的更多相关文章

  1. 理解与模拟一个简单servlet容器

    servlet接口 使用servlet编程需要实现或者继承实现了javax.servlet.Servlet接口的类,其中定义了5个签名方法: public void init(ServletConfi ...

  2. Tomcat详解系列(1) - 如何设计一个简单的web容器

    Tomcat - 如何设计一个简单的web容器 在学习Tomcat前,很多人先入为主的对它的认知是巨复杂的:所以第一步,在学习它之前,要打破这种观念,我们通过学习如何设计一个最基本的web容器来看它需 ...

  3. 从零构建一个简单的 Python Web框架

    为什么你想要自己构建一个 web 框架呢?我想,原因有以下几点: 你有一个新奇的想法,觉得将会取代其他的框架 你想要获得一些名气 你遇到的问题很独特,以至于现有的框架不太合适 你对 web 框架是如何 ...

  4. java框架之SpringBoot(8)-嵌入式Servlet容器

    前言 SpringBoot 默认使用的嵌入式 Servlet 容器为 Tomcat,通过依赖关系就可以看到: 问题: 如何定制和修改 Servlet 容器相关配置? SpringBoot 能否支持其它 ...

  5. 一个简单的Servlet容器实现

    上篇写了一个简单的Java web服务器实现,只能处理一些静态资源的请求,本篇文章实现的Servlet容器基于前面的服务器做了个小改造,增加了Servlet请求的处理. 程序执行步骤 创建一个Serv ...

  6. how tomcat works 读书笔记(二)----------一个简单的servlet容器

    app1 (建议读者在看本章之前,先看how tomcat works 读书笔记(一)----------一个简单的web服务器 http://blog.csdn.net/dlf123321/arti ...

  7. 一个简单的servlet容器

    [0]README 0.1)本文部分文字转自 “深入剖析Tomcat”,旨在学习  一个简单的servlet容器  的基础知识: 0.2)for complete source code, pleas ...

  8. Tomcat学习笔记(二)—— 一个简单的Servlet容器

    1.简介:Servlet编程是通过javax.Servlet和javax.servlet.http这两个包的类和接口实现的,其中javax.servlet.Servlet接口至关重要,所有的Servl ...

  9. 使用Servlet和JSP实现一个简单的Web聊天室系统

    1 问题描述                                                利用Java EE相关技术实现一个简单的Web聊天室系统,具体要求如下. (1)编写一个登录 ...

随机推荐

  1. Kettle 执行SQL脚本

    以下操作都在5.0.1版本下进行开发,其余版本可以进行自动比对 本文将对Kettle5中常用步骤字段选择(又名选择/改名值,英文原名:Select Values)进行详细解释.这个步骤的功能非常强大, ...

  2. 小程序的flex布局

    小程序建议使用flex布局进行排版 flex就是一个盒装弹性布局 flex是一个容器,所有子元素都是他的成员 小程序的flex布局 定义布局 display:flex flex容器的属性: flex- ...

  3. 环形链表 II

    给定一个链表,返回链表开始入环的第一个节点. 如果链表无环,则返回 null. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始). 如果 pos 是 - ...

  4. day71_10_16多表断关联

    ---恢复内容开始--- 本次环境: 配置settings INSTALLED_APPS = [ # ... 'rest_framework', ] DATABASES = { 'default': ...

  5. 《深度学习》圣经"花书"经验法则中文版!

    作者:Jeff Macaluso https://jeffmacaluso.github.io/post/DeepLearningRulesOfThumb/ 转自CVer,仅用作个人学习 当我在研究生 ...

  6. 消息中间件(二)MQ使用场景

    一.消息队列概述 消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构.目前使用较多的消息队列有ActiveMQ,Rabbit ...

  7. luoguP2463 [SDOI2008]Sandy的卡片

    题意 显然加上一个数相等就是差分数组相等,于是问题变为求几个串的最长公共子串. 这里我学习了如何用SA求LCS. 首先问题要转化成求一些后缀的最长公共前缀,要求这些后缀分属不同的串. 于是二分答案,于 ...

  8. 使用nexus搭建maven仓库(maven 本地私服)

    我们在使用maven的时候,对于项目所依赖的jar包,maven默认会在中央仓库下载jar包,到本地的磁盘目录(如果没有配置则是用户目录下/.m2/repository文件夹下).如果公司内部搭了一个 ...

  9. VBA基础 - 分支和循环

    概要 编程语言的基础除了数据类型, 就是控制结构了. 所谓控制结构, 主要就是分支和循环. 分支 废话不说, 直接示例代码: 单条件判断 1 Sub Test() 2 If 2 > 1 Then ...

  10. Qt 编译配置相关总结

    MinGW 与 MSVC 编译的区别 我们可以从 Qt 下载页面看到两种版本编译器,如下图: 我们来对比一下这两个编译器的区别: MSVC 是指微软的 VC 编译器. MinGW 是 Minimali ...