2.2 应用程序 1

下面从servlet容器的角度审视servlet程序的开发。简单来说,对一个Servlet的每个HTTP请求,一个功能齐全的servlet容器有以下几件事要做:
当第一次调用某个servlet时,要载入该servlet类,并调用其init()方法(仅此一次);
针对每个request请求,创建一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例;
调用该servlet的service()方法,将servletRequest对象和servletResponse对象作为参数传入;
当关闭该servlet类时,调用其destroy()方法,并卸载该servlet类。
本章所要建立的servlet容器是一个很小的容器,没有实现所有的功能。因此,它只能运行非常简单的servlet,而且也会不调用servlet的init()和destroy()方法。它会做以下几件事:
等待HTTP请求;
创建一个servletRequest对象和一个servletResponse对象;
若请求的是一个静态资源,则调用StaticResourceProcessor对象的process()方法,传入servletRequest对象和servletResponse对象;
若请求的是servlet,则载入相应的servlet类,调用其service()方法,传入servletRequest对象和servletResponse对象。
注意 在该servlet容器中,每次请求servlet都会载入相应的servlet类。
本节的应用程序包括6个类:
HttpServer1
Request
Response
StaticResourceProcessor
ServletProcessor1
Constants
图2-1展示了本节中的servlet容器的UML类图。
该应用程序的入口点(静态main()方法)在类HttpServer1中。main()方法创建HttpServer1的一个实例,然后调用其await()方法。await()方法会等待HTTP请求,为接收到的每个请求创建一个Request和一个Response对象,并根据该HTTP请求的是静态资源或是servlet,将该HTTP请求分发给一个StaticResourceProcessor实例或一个ServletProcessor实例。
Constants类中定义了静态final WEB_ROOT,供其他的类引用。WEB_ROOT指定了该servlet容器中使用的PrimitiveServlet类和静态资源的位置。
HttpServer1类的实例会一直等待HTTP请求,直到接收到一条关闭命令。可以使用第1章介绍的方法来发布关闭命令。
该应用程序中的各个类会在接下来的几节中逐个说明。

2.2.1 HttpServer1类

应用程序1中的HttpServer1类与第1章中简单Web服务器应用程序中的HttpServer类似。但是,该应用程序中的HttpServer1类既可以对静态资源请求,也可以对于servlet资源请求。若要请求一个静态资源,可以在浏览器的地址栏或URL框中输入如下格式的URL:

http://machineName:port/staticResource

这与第1章的Web服务器应用程序中对静态资源的请求相同。
若要请求servlet资源,可以使用如下格式的URL:

http://machineName:port/servlet/servletClass

因此,若要请求本地浏览器上的名为PrimitiveServlet的servlet,可以在浏览器的地址栏或URL框中输入如下的URL:

http://localhost:8080/servlet/Primitiveservlet

应用程序1中的servlet容器会处理对PrimitiveServlet的请求。但是,若要调用其他的servlet(如ModernServlet),则servlet容器抛出异常。在后面的章节中,你将学会如何构建可以兼具两种功能的servlet容器。
HttpServer1类的定义在代码清单2-2中。



该类的await()方法会一直等待HTTP请求,直到接收到一条关闭命令,这点与第1章中的await()方法类似。区别在于,本章中的await()方法可以将HTTP请求分发给StaticResourceProcessor对象或ServletProcessor对象来处理。当URI包含字符串“/servlet/”时,会把请求转发给servletProcessor对象处理。否则的话,把HTTP请求传递给StaticResourceProcessor对象处理。注意代码清单2-2中灰色的部分。

2.2.2 Request类

servlet的service方法会从servlet容器中接收一个javax.servlet.ServletRequest实例一个和javax.servlet.ServletResponse实例。即,对每个HTTP请求来说,servlet容器必须创建一个ServletRequest对象和一个ServletResponse对象,并将它们作为参数传给它服务的servlet的service()方法。
ex02.pyrmont.Request类表示被传递给servlet的service()方法的一个request对象。它必须实现javax.Servlet.servletRequest接口中声明的所有方法。但为了简单起见,这里只给出了部分方法的实现,其余方法的实现会在后面的章节给出。为了能够编译Request类,需要将未实现的方法留空。代码清单2-3中给出了Request类的定义,其签名返回obejct实例的所有方法都会返回null。




此外,Request类还包括了在第1章中介绍过的parse()和getUri()方法。

2.2.3 Response类

ex02.pyrmont.Response类实现javax.servlet.servletResponse接口,类定义参见代码清单2-4。该类提供了servletResponse接口中声明的所有方法的实现。与Request类类似,除了getWriter()方法以外,大部分方法的实现都留空。


在getWriter()方法中,PrintWriter类的构造函数的第2个参数是一个布尔值,表示是否启用autoFlush。对第2个参数传入true表示对println()方法的任何调用都会刷新输出。但是调用print()方法不会刷新输出。
因此,如果在servlet的service()方法的最后一行调用print()方法,则该输出内容不会被发送给浏览器。这个bug会在后续的版本中修改。
Response类中仍然保留了第1章中介绍过的sendStaticResource()方法。

2.2.4  StaticResourceProcessor类

ex02.pyrmont.StaticResourceProcessor类用于处理对静态资源的请求。该类只有一个方法,即process()方法。代码清单2-5给出了StaticResourceProcessor类的定义。

process()方法接收两个参数:一个ex02.pyrmont.Request实例和一个ex02.pyrmont.Response实例。该方法仅仅调用Response对象的sendStaticResource()方法。

2.2.5 servletProcessor1类

ex02.pyrmont.servletProcessor1类的定义参见代码清单2-6,该类用于处理对servlet资源的HTTP请求。
代码清单2-6 servletProcessor1类的定义

package ex02.pyrmont; import java.net.URL;
import java.net.URLClassLoader;
import java.net.URLStreamHandler;
import java.io.File; import java.io.IOException;
import javax.servlet.Servlet;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse; 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. 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()); } }
}

servletProcessor1类很简单,只有一个方法:process()方法。该方法接收两个参数,一个javax.servlet.ServletRequest实例和一个javax.servlet.ServletResponse实例。该方法通过调用getRequestUri()方法从ServletRequest对象中获取URI:

String uri = request.getUri();

记住,URI的格式如下所示:

/servlet/servletName

其中,servletName是请求的servlet资源的类名。
为了载入servlet类,需要从URI中获取servlet的类名。可以使用process()方法的下一行语句获取servlet的类名:

String servletName = uri.substring(uri.lastIndexOf("/") + 1);

接下来,porcess()方法会载入该servlet类。为了载入类,需要创建一个类载入器,并且指明到哪里查找要载入的类。对于本节的servlet容器,类载入器会到Constant.WEB_ROOT指定的工作目录下的webroot目录中查找要载入的类。
注意 有关类载入器的详细内容将在第8章中介绍。
为了载入一个servlet类,可以使用java.net.URLClassLoader类来完成,该类是java.lang.ClassLoader类的一个直接子类。一旦创建了URLClassLoader类的实例后,就可以使用它的loadClass()方法来载入servlet类。实例化URLClassLoader类很简单。该类有三个构造函数,其中比较简单的一个构造函数的签名如下所示:

public URLClassLoader(URL[] urls);

其中,urls是一个java.net.URL对象数组,当载入一个类时每个URL对象都指明了类载入器要到哪里查找类。若一个URL以“/”结尾,则表明它指向的是一个目录。否则,URL默认指向一个JAR文件,根据需要载入器会下载并打开这个JAR文件。
注意 在servlet容器中,类载入器查找servlet类的目录称为仓库(repository)。
在应用程序中,类载入器只需要查找一个位置,即工作目录下的webroot目录。因此,需要先创建只有一个URL的一个数组。URL类提供了一系列构造函数,因此有很多种方法可以创建URL对象。对于本应用程序,使用与Tomcat中另一个类中使用的相同构造函数,该构造函数的签名如下所示:

public URL(URL context, java.lang.String spec, URLStreamHandler hander)  throws MalformedURLException

可以为第2个参数指定一个目录,指定第1个和第3个参数为null,这样就可以使用构造函数了。但是还有一个构造函数,它接受3个参数:
public URL(java.lang.String protocol, java.lang.String host, java.lang.String file) throws MalformedURLException
因此,若只使用如下语句,编译器就无法知道要调用哪个构造函数了,并且会报错:

new URL(null, aString, null);

因此,可以使用下面的代码,对于编译器指明第三个参数的类型:

URLStreamHandler streamHandler = null;
new URL(null, aString, streamHandler);

第2个参数中的字符串指明了仓库的路径,也就是查找servlet类的目录。可以使用下面的代码生成仓库:

String repository = (new URL("file", null, classPath.getCanonicalPath() + File.separator)).toString() ;

将上述代码综合到一起,就得到了创建URLCLassLoader实例的process()方法的部分代码:

注意 生成仓库后会调用org.apache.catalina.startup.ClassLoaderFactory类的createClassLoader()方法,生成URL对象后会调用org.apache.catalina.loader.StandardClassLoader类的addRepository()方法。这些方法将在后续章节中介绍。
有了类载入器后,就可以通过调用loadClass()方法来载入servlet类:

接下来,process()方法会创建已载入的servlet类的一个实例,将其向下转型为javax.servlet.servlet,并调用其service()方法:

2.2.6 运行应用程序

要在Windows平台上运行该程序,可以在工作目录下执行如下命令:

java -classpath ./lib/servlet.jar;./ ex02.pyrmont.HttpServer1

在Linux平台上,需要用冒号分割两个库文件:

java -classpath ./lib/servlet.jar:./ ex02.pyrmont.HttpServer1

若想测试应用程序,可以在浏览器的地址栏或者URL框中输入如下地址:

http://localhost:8080/index.html

http://localhost:8080/servlet/Primitiveservlet

当调用PrimitiveServlet类时,可以在浏览器中看到如下输出:

Hello. Roses are red.

注意,你是看不到第2个字符串“Violets are blue”的,因为只有第1个字符串会发送到浏览器。这个问题将在第3章解决。

《深入剖析Tomcat》一2.2 应用程序 1相关推荐

  1. 【深入剖析Tomcat笔记】第一篇 基础知识储备

    基础知识储备 最近突然在想,做了这么久的WEB相关,像tomcat.apache这些服务器究竟是什么东西,恰好碰到<How Tomcat Works>(中文版<深入剖析Tomcat& ...

  2. 深入剖析Tomcat(How Tomcat Works)

    <深入剖析Tomcat>前言(作者:Budi Kurniawan & Paul Deck) 欢迎阅读本书.本书剖析了Tomcat 4.1.12版本和Tomcat 5.0.18版本的 ...

  3. Java后台开发Tomcat添加https支持小程序开发过程

    文章原文:blog.ouyangsihai.cn >> Java后台开发Tomcat添加https支持小程序开发过程 1 给自己的域名申请证书 注意:申请好了如果不是在腾讯注册的域名,不会 ...

  4. tomcat web应用_具有可执行Tomcat的独立Web应用程序

    tomcat web应用 在部署应用程序时,简单性是最大的优势. 您将了解到,尤其是在项目发展且需要在环境中进行某些更改时. 将您的整个应用程序打包到一个独立且自足的JAR中似乎是个好主意,特别是与在 ...

  5. 具有可执行Tomcat的独立Web应用程序

    在部署应用程序时,简单性是最大的优势. 您将了解到,尤其是在项目发展且需要在环境中进行某些更改时. 将整个应用程序打包到一个独立且自足的JAR中似乎是个好主意,尤其是与在目标环境中安装和升级Tomca ...

  6. 如果在docker中部署tomcat,并且部署java应用程序

    2019独角兽企业重金招聘Python工程师标准>>> 如果在docker中部署tomcat,并且部署java应用程序 博客分类: docker 1.先说如何在docker中部署to ...

  7. java none怎么用tomcat_在docker中部署tomcat并且部署java应用程序的步骤详解

    先给大家简单说下Docker的概念 Docker 是一个开源的应用容器引擎,让开发者可以打包他们的应用以及依赖包到一个可移植的容器中,然后发布到任何流行的 Linux 机器上,也可以实现虚拟化.容器是 ...

  8. 《深入剖析Tomcat》源码

    <深入剖析Tomcat>翻译自<How Tomcat Works> 可以到官网下载:https://brainysoftware.com/download 官网下载比较慢,我就 ...

  9. 用Eclipse远程调试部署在Tomcat下的Web应用程序

    今天同事告诉了我一个用Eclipse远程调试部署在Tomcat下的Web应用程序的方法: 安装解压版本的Tomcat,因为现在好像Tomcat的Windows安装版目录下没有catalina.sh(. ...

最新文章

  1. 探索JAVA并发 - 悲观锁和乐观锁
  2. Jquery调用webservice
  3. 信号与系统参考书推荐
  4. IPv6协议简介及与IPv4对比
  5. nginx配置多个站点共用80端口
  6. 作者:陈振冲(1959-),男,博士,香港理工大学学务长,电子计算学系教授...
  7. Nemo(Nightwish乐队)
  8. Unity网络--HLAPI(2):Unity HLAPI NetworkManager与NetworkBehaviour的回调函数
  9. IDEA - 解决“idea 环境:junit:4.11 测试 mybatis 代码,无法导入 org.junit.Test 包”问题
  10. 使用jps查看JAVA进程号
  11. 各种数据库之间数据迁移工具
  12. 数独高阶技巧之八——SDC
  13. 列表解析式,生成表达式
  14. Linux 生成so库文件并调用
  15. 数据管理能力成熟度模型(DMM)
  16. node安装与环境搭建 + VUE项目搭建
  17. 可扩展区块链的墨客实践——硅谷陈小虎
  18. 100本书的100句话,句句直刺心底!
  19. 【BZOJ4668】冷战(并查集)
  20. 转载Python正则表达式匹配反斜杠'\'问题(——字符串转义与正则转义)

热门文章

  1. SpringBoot中使用POI实现自定义Excel布局式导出
  2. 基于用例点来度量软件规模并管理进度 之二
  3. CMMI之需求管理和股票池管理
  4. 论大型信息系统集成项目的整体管理
  5. Unity之Math等方法的使用
  6. cdh5.12.1 service monitor监控状态_来,我们在重新说下,线程状态?
  7. 专访卢政:TSF如何助力企业微服务开发
  8. Linux解决openoffice转换PDF乱码问题(ubutun16.0.4)
  9. YouTube增加社交功能:邀请联系人聊天 可30人群聊
  10. BFC与IFC概念理解+布局规则+形成方法+用处