CHAPTER 2 A Simple Servlet Container

  • Servlet编程的实现依赖两个包:javax.servlet和javax.servlet.http.其中javax.servlet.Servlet接口是最重要的。所有的Servlet类都要实现该接口,或者继承实现了该接口的类
  • 一个servlet包含五大方法:
    • public void init(ServletConfig config) throws ServletException
    • public void service(ServletRequest request, ServletResponse response) throws SerevletException, java.io.IOException
    • public void destroy()
    • public ServletConfig getServletConfig()
    • public java.lang.String getServetInfo()

     

    • 其中方法init(),service(),destroy()三个方法标示了一个servlet的生命周期
    • 当servlet被实例化之后,由servlet容器负责调用init()方法,该方法仅被调用一次,用来表明该servlet已经被加载进服务中了,因此该方法的默认实现通常是空实现;我们可以重写init()方法,在init()方法中加载一些仅需执行一次的初始化代码,如:加载数据库驱动,初始值,等等
    • 当客户端发起一次servlet请求时,servlet容器会调用service()方法,并将javax.servlet.ServletRequest和javax.servlet.ServetResponse两个对象传递过去;javax.servlet.ServletRequest对象中包含了客户端的HTTP请求信息,javax.servlet.ServletResponse对象封装了对于本次servlet请求的响应信息。service()方法在当前servlet活动期间可以被多次调用执行
    • 在servlet被移出servlet容器停止服务之前,servlet容器会调用destroy()方法。该方法的调用通常发生在servlet容器停止提供服务时或servlet容器的空间不足需移出部分servlet实例时;从另一方面来说,该方法也为我们提供了一种清理当前servlet所持有资源(比如内存空间、文件句柄、线程等)以确保操作对象的持久化状态与内存中servlet的当前状态一致的渠道
  • 一个完整功能的Servlet容器,对于每一个HTTP的servlet请求来到时,所要进行的操作:
    1. 当请求的servlet是首次被调用时,加载请求的servlet并调用servlet的init()方法
    2. 对于每一次请求,都要生成一个javax.servlet.ServletRequest对象和javax.servlet.ServletResponse对象
    3. 调用servlet的service()方法,并将创建的javax.servlet.ServletRequest对象和javax.servlet.ServletResponse对象传递过去
    4. 当servlet的生命周期结束时,要调用servlet的destroy()方法并从servlet容器中移除servlet
  • 代码解析:
  • ——————————————————————————————————————————————
总述:
  • 应用一:
  • ========================================================================================
    • 首个应用中,servlet容器仅能运行简单的Servlet,并且servlet不会调用init()方法和destroy()方法,所作的工作如下:
    • 等待客户端HTTP请求的接入
    • 构造ServletRequest对象和ServletResponse对象
    • 若请求静态资源,则调用StaticResourceProcessor类的process()方法,并将上一步中构造的两个对象传递到该方法中
    • 如请求Servlet,则加载请求的Servlet并调用service()方法,并传递SerevletRequest和ServletResponse对象

注:在每次请求servlet时,请求的servlet都会被重新加载

  • 所包含的类:
  • HttpServer1
  • Request
  • Response
  • StaticResourceProcessor
  • ServletProcessor1
  • Contants
  • 各个类之间的关系:
  • HttpProcessor1:
    • 在main方法中创建一个HttpProcessor1的实例,并调用await()方法
    • 在await()方法中,创建一个ServerSocket对象,等待客户端HTTP请求的接入
    • 当接收到一个HTTP请求时,分配给一个Socket,并创建此次请求对应的Request和Response对象,根据不同的请求调用不同的处理器的process()方法进行处理,之后关闭Socket,并判断请求的URI是否是一个关闭命令,若是,HttpProcessor1实例的生命周期结束
    • Request对象实例化:将此次请求的输入流对象(通过socket获得)作为构造参数传递过去,之后调用parse()方法,填充request对象的其他参数属性
    • Response对象实例化:将此次请求的输出流对象(通过socket获得)作为构造参数传递过去,之后通过setRequest()方法将request设置进去
    • 不同的请求(通过URI判断)的处理:
      • 若请求的静态资源,则创建StaticResourceProcessor实例,将request和response作为参数调用process()方法
      • 若请求的是servlet,则创建ServletProcessor实例,将request和response作为参数调用process()方法
  • Request:
    • request作为参数之一被传递到service()方法,因此它必须实现javax.servlet.ServletRequest接口,在本例中仅进行了简单的“空实现”
    • 除继承自ServletRequest接口中的方法之外,还添加了一下方法和类属性以用于业务逻辑:
      • 属性:<InputStream> input和<String> uri
      • 方法:<public>parse()、<private>parseUri()和<public>getUri()

 

  • Response:
    • response作为另一参数被传递到service()方法,因此它要实现javax.servlet.ServletResponse接口,如同request一样,除了getWriter()方法之外,其它均是“空实现”
    • 方法:sendStaticResource()方法用于当请求的是静态资源时,来提供服务
    • getWriter()方法,返回的是一个PrintWriter对象的实例,它的第二个参数:true,表示调用对象的println()方法时会自动清理缓存(即可以不用调用flush()方法),但是print()方法并不会,因此如果service()方法的最后一行调用的是print()方法,若写出的信息量比较小的话,那么在客户端(浏览器)是看不到输出的

 

  • ServletProcessor1:
    • 该类的实现比较简单,仅包含一个process()方法
    • 给方法在本例中所作的工作:
      • 从请求的uri中获取用户请求的Servlet的名字
      • 创建一个类加载器,将请求的Servlet类加载到内存中,然后创建一个该Servlet的实例,调用service()方法提供服务
      • Servlet类的加载:
        • 在本例中是通过Servlet名字进行类的加载的,所以在请求Servlet时,必须与自定义的那个Servlet类的名字相一致
        • 类加载器:URLClassLoader,它有四个构造函数,最简单的一个:

public URLClassLoader(URL[] urls) //需要一个URL的对象数组作为构造参数

  • 一个URL,若以"/"结尾,则它指向一个目录的地址;反之,它指向一个可以打开或下载的JAR文件的地址
  • 在Servlet容器中,类加载器所加载的那个Servlet类的位置称为"库"
  • URL包含很多的构造函数,这里采用与Tomcat中相同的构造函数:

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

本例中只需要指定第二个参数的值,第一和第三个置空即可

但是URL还有另一个构造函数:

public URL(java.lang.String protocol, java.lang.String host, java.lang.String file) throws MalformedURLException

所以,若只指定第二个参数,就不知道调用哪个构造函数了,所以为了区分,这里我们指定了一下第三参数的类型:

URLStreamHandler streamHandler = null;

new URL(null, repository, streamHandler);

  • 指定第二个参数的值:

File classPath = new File(Constants.WEB_ROOT);

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

  • 创建Servlet实例,调用Service()方法:
    Class clazz = classLoader.loadClass(servletName);
    Servlet servlet = (Servlet) clazz.newInstance();
    servlet.service((ServletRequest)request, (ServletResponse)response);
    

  • ========================================================================================
  • 应用二:
  • ========================================================================================
    • 目的在于修补【应用一】中的可能的Bug:在ServletProcessor1调用Servlet的service()方法时,需要将request和response对象向上转型为javax.servlet.ServletRequest和javax.servlet.ServletResponse才能作为参数传递到service()方法中
    • 这违背了安全性原则,因为对于熟悉Servlet容器内部工作原理的程序员来说,他就可以手动的将ServletRequest对象实例和ServletResponse对象实例向下转型为Request和Response对象实例,从而可以调用Request和Response类中的外部接口方法(public),如:Request中的parse()方法和Response中的sendStaticResource()方法,但是这些方法在Servlet中又不应该是可见的,也不能将这些方法的作用域置为私有的(private),因为这些方法在其它的类中需要被调用
    • 有两种解决方式:
      • 方式一:将Request和Reponse中的这些方法的访问限定符改为默认的
      • 方式二:使用门面设计模式(Façade Design Schema)
    • 这里采用第二种解决方式:

 

  • 在此以RequestFacade为例:
    • RequestFacde与Request同样实现ServletRequest接口
    • 在RequestFacde中定义一个私有的所实现接口的引用变量,通过构造函数的方式进行初始化赋值
    • 将一个Request的对象实例作为参数向RequestFacade传递过去

这样就可以避免上面阐述的安全隐患问题了

 

说明:当将RequestFacade向上转型为ServletRequest时,所有调用的对ServletRequest接口的方法实现都是Request对象中的实现,而在RequestFacade中,虽然接收了一个Request对象赋给了一个ServletRequest变量引用,但是该变量是私有的(private),在类的外部是不能对其进行操作的(如类型强转为Request类型),这样Request中的公有方法在Servlet中就被保护起来了

  • 应用中所包含的类:
    • HttpServer2
    • Request
    • Response
    • StaticResourceProcessor
    • ServletProcessor2
    • Constants
  • ServletProcessor2中不同的代码部分:
Servlet servlet = null;

RequestFacade requestFacade = new RequestFacade(request);

ResponseFacade responseFacade = new ResponseFacade(respoonse);

try {

	Servlet = (Servlet)myClass.newInstance();

	Servlet.service((ServletRequest)requestFacade, (ServletResponse)responseFacade);

}


 

  • 门面类(以RequestFacade为例):
public RequestFacade implements javax.servlet.ServletRequest {

	private ServletRequest request;

	public RequestFacade(Request request) {

		this.request = request;

	}

 

	/* implementation of the ServletRequest */

	Public Object getAttribute(String attribute) {

		Return request.getAttribute(attribute);

	}	

	......

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值