第一章Servlet基础

来源:互联网 发布:澳柯玛集团数据 编辑:程序博客网 时间:2024/06/06 00:16

一.web中间件作用:

1. 读取客户程序发送的显示数据2. 读取浏览器发出的隐式HTTP请求数据:cookie,媒体类型,浏览器能够识别的压缩模式等。3. 生成结果:与数据库对话等。4. 向客户发送显示数据(即文档):html,xml,gif图像,excel,gzip等。5. 发送隐式HTTP响应数据:告知浏览器或其他客户程序发挥文档的类型(如html),这支cookie并缓存参数及其他类似任务。

二.servlet基础:

  • doGet:处理GET请求(用户在浏览器地址栏输入的url,点击web内页面的链接,提交没有指定method或method=”GET”的html表单)
  • HttpServletRequest request:通过它得到所有输入的数据,如表单数据,HTTP请求报头,客户的主机名等信息。
  • HttpServletResponse response:通过它指定输出信息,如HTTP状态码(200,404等),响应报头(Content-Type,Set-Cookie等),最重要的是,可以获得PrintWriter,用它可以将文档内容发送给用户。
    • Http响应报文:状态行+一个或多个报头+一个空行+实际的文档以此次序构成。
    • 关于报头:报头的设置次序并不重要,servlet会缓冲报头数据,将它们一次发送到客户端,因此,即使设定报头之后依旧可以设定状态代码(属于返回内容的第一行),但是servlet不一定会缓冲文档本身,最好还是将setContentType行放在PrintWriter之前。
    • 关于缓冲文档:servlet引擎可以缓冲部分输出,但并未规定缓冲区的大小,可以使用HttpServletResponse的getBufferSize方法获取缓冲区大小,setBufferSize设置缓冲区大小,isCommitted检查缓冲区是否已经发送出去,因此可以在缓冲区填满,要发往客户时再设置报头,但最佳方案还是在PrintWriter的行之前设置报头。
  • servlet生成html:
    1. 告知浏览器即将向它发送html:response.setContentType(“text/html”); 一般报头使用HttpServletResponse的setHeader方法来设置,但由于设置内容的类型是一项十分常见的任务,因而,HttpServletResponse提供特殊的setContentType方法专门用于这种目的。常见的内容类型如下:
      • html:text/html
      • excel文件:application/vnds.ms.excel
      • JPEG图像:image/jpeg
      • XML文档:text/xml
    2. 使用PrintWriter的println语句,构建合法的web页面:
    3. 用形式语法验证器(formal syntax validator)检查生成的html:向验证器提供url,验证器返回错误,验证器提供方:万维网联盟:http://validator.w3.org/,Web Design Group:http://www.htmlhelp.com/tools/validator/
html文档结构:<!DOCTYPE html><HTML>    <HEAD>        <TITLE>...</TITLE>        ...    </HEAD>    <BODY>        ...    </BODY></HTML>
html文档生成代码:response.setContentType("text/html");response.setStatus(200);PrintWriter out = response.getWriter();String docType = "<!DOCTYPE html>\n";//DOCTYPE属性:告诉html验证器我们使用的是哪个版本的htmlStringBuilder sb = new StringBuilder();sb.append(docType).append("<HTML>\n")                .append("<HEAD><TITLE>JSP Test</TITLE></HEAD>\n")                .append("<BODY>\n")                .append("<H1>"+ new Date() +"</H1>\n")                .append("</BODY>\n</HTML>");out.println(sb.toString());

三.servlet生命周期

  • servlet一般在用户首次调用对应URL时被创建,但也可以指定servlet在服务器启动后载入。
  • 服务器只创建每个servlet的单一实例,实例创建时它的Init方法会被调用,创建完成之后,针对每个用户请求都会引发新的线程,该线程调用前面创建的servlet实例的service方法,service方法再将用户请求交给相应的doGet或doPost处理。
  • 流程如下:
    这里写图片描述

  • Init方法

    • 常规初始化:将数据文件载入HashMap,建立数据库连接等由
    • 参数控制的初始化:如从配置文件载入数据库连接等,调用 getServletConfig().getInitParameter(“name”)读取设定在web.xml中的servlet元素内的init-param子元素的参数完成servlet初始化。
  • service方法:服务器每次接到对servlet的请求,都会产生一个新的线程,在由改线程调用service方法,service方法检查HTTP的请求类型(GET,POST,PUT,DELETE等)并相应地调用doGet,doPost,doPut,doDelete等方法。

    • GET请求起因于正常的url请求或没有指定method的html,POST请求起因于将POST列为METHOD的html表单。
    • 如果要同等的处理GET与POST请求,别重写service,这样PUT,DELETE等请求无法得到处理,让doGet调用doPost这样写更好。
  • destroy方法:servlet被销毁时调用,处理关闭数据库,存储变量值等清理活动。

  • SingleThreadModel:如果servlet实现了这个接口,那么,大多数情况下,系统会将所有请求排队,一次只将一个请求转给单个servlet实例。如果servlet被频繁访问,那么这么做会造成性能的极大损害。当然可以建立一个servlet的实例池生成多个servlet来处理并发,不过如果servlet中使用到静态变量依旧会面临共享资源的并发访问问题。

四.多线程并发访问servlet的共享资源的竞争问题:

  • 问题如下:
    如果一个servlet使用下面这段代码为每个用户指定一个ID用于跟踪用户接下来的情况,正常情况下一个用户会有唯一的ID用于标示,但如果在第一个用户读取了nextID后,在nextID自增长前,它的执行权被别的线程抢占,第二个用户有可能会读取该字段,从而获得与第一个客户相同的nextID,这种问题可能会使电子商务程序偶尔会用错误的客户信用卡支付客户商品的情况产生。
String id = "User-ID-" + nextID;nextID = nextID + 1;
  • 解决方案

    1. 减少竞争:将代码改为:String id = “User-ID-” + nextID++;这种方案降低了问题产生的可能性,不过并没有完全消除,很多情况下,降低错误答案出现的可能性是一件坏事情而不是一件好事情,这种方案只是意味着问题更难在测试中被发现,而在真正应用中更易于发生。

    2. 使用SingleThreadModel,如果使用这种方式,多个servlet的nextID是互相独立的,产生相通nextID是可以预见的事情。

    3. 最佳:使用synchronized明确的同步对代码块的访问,一旦一个线程进入下面的代码块(或任何其他用同一对象应用标记的synchronized段)中,则直到第一个线程退出为止,不允许任何其他线程进入该代码段。java编程中经常使用该方式处理竞争问题。
synchronized (this){    String id = "User-ID-" + nextID++;    nextID = nextID + 1;}