作为Servlet
Servlet的存在就是为客户服务
Servlet 技术模型
1.1 对于每一种HTTP方法(如get、post、head等),描述该方法的用途,以及该HTTP方法协议的技术特性,并列出客户(通常是一个Web浏览器)会因为哪些原因使用这种方法,明确对应这种HTTP方法的HttpServlet方法。
1.2 使用HttpServletRequest接口,编写代码从请求获取 HTML 表单参数,获取HTTP 请求首部信息,或者从请求获取cookie。
1.3 使用HttpServletResponse接口,编写代码设置 HTTP 响应首部,设置响应内容类型,为响应获得一个文本流,为响应获得一个二进制流,把一个HTTP 请求重定向到另一个URL,或者向响应 增加cookie 。
1.4 描述serlvet生命周期的作用和事件序列
- serlvet类加载
- servlet实例化
- 调用init()方法
- 调用service方法
- 调用destroy()方法
Serlvet 的生命周期
Servlet 继承树
Servlet 生命周期的三大重要时刻
方法 | 何时调用 | 作用 | 是否覆盖 |
---|---|---|---|
init() | serlvet实例创建后,并在serlvet能为客户请求提供服务前,容器要对serlvet调用init() | 使你在serlvet处理客户请求之前有机会对其初始化 | 有可能 |
service() | 第一个客户请求到来是,容器会开始一个新线程,或者从线程池分配一个线程,并调用servlet的service方法 | 这个方法会查看请求,确定HTTP方法(get、post等),并在servlet上调用对应的方法 | 不太可能 |
doGet()或doPost() | service()方法根据请求的HTTP方法(get、post等)来调用doGet()或doPost | 写自己的业务逻辑 | 至少覆盖其中之一 |
注意:是开启一个service()线程,也就是容器为每一个请求都开启一个独立的线程。
构造函数不足以初始化servlet
在调用构造函数和init()方法之间,servlet处在一种薛定谔servlet状态。如果过早的初始化代码会导致失败,所以需要init()方法。
从servlet对象到一个真正的servlet
拥有2样东西
- ServletConfig对象
- 每个Servlet都有一个ServletConfig对象
- 用于向Servlet传递部署时信息(例如数据库名),而你不想把这个信息硬编码写到servlet中(servlet初始化参数)
- 用于访问ServletContext
- 参数在部署描述文件中配置(DD)
- ServletContext
- 每个Web应用有一个ServletContext(应该叫做AppContext才对)
- 用于访问Web应用参数(DD中配置)
- 相当于一种应用公告栏,可以在这里放置消息(称为属性),应用的其他部分可以访问这些消息
- 用于得到服务器信息,包括容器名和容器版本,以及所支持的API的版本等
请求的UML
响应的UML
谁来实现HttpServletRequest和HttpServletResponse接口?这些类在API中吗?
第一个答案是容器,第二个答案是“不”。
Http方法
方法 | 说明 |
---|---|
GET | 要求得到所请求URL上的一个东西(资源/文件) |
POST | 要求服务器接收附加到请求的体信息,并提供所请求URL上的一个东西 |
HEAD | 只要求得到GET返回结果的首部部分 |
TRACE | 要求请求消息回送,这样客户能看到另一端上接收了什么,以便测试或排错 |
PUT | 指出要把所包含信息(体)放在请求的URL上 |
DELETE | 指出删除所请求URL上的一个东西(资源/文件) |
OPTIONS | 要求得到一个HTTP方法列表,所请求URL上的东西可以对这些HTTP方法做出响应 |
CONNECT | 要求连接以便建立隧道 |
幂等
对于HTTP/servlet来说“幂等”这个这个词表示同一个请求可以做出两次,而不会对服务器产生负面影响(所谓的影响可以是更改数据库中的数据)。
这里指出POST是“非幂等”的,并且可以是GET也成为“非幂等”。
如何使浏览器发送POST或者GET
很多种方法,主要方法有:
- form表单中的method属性设置为相应的方法
- ajax发送相应方法的请求
- a标签的href默认为get方法
getServerPort()、getLocalPort()和getRemotePort()区别
getServerPort() | getLocalPort() | getRemotePort() |
---|---|---|
获得服务器端口 | 获得service()线程的本地端口 | 获得客户端端口 |
复习:servlet生命周期和API
- 容器要加载类、调用servlet的无参构造函数、并调用servlet的init()方法,从而初始化servlet。
- init()方法(开发人员可以覆盖)在servlet一生中只调用一次,往往在servlet为客户请求提供服务之前调用。
- init()方法使servlet可以访问ServletConfig和ServletContext对象,servlet需要从这些对象的恩爱到有关servlet配置和Web应用的信息。
- 容器通过调用servlet的destroy()方法来结束servlet的生命。
- servlet一生的大多数时间都是在为某个客户请求运行service()方法。
- 对servlet的每个请求都在一个单独的线程中运行,任何特定servlet类都只有一个实例。
- 你的servlet一般都会扩展javax.servlet.http.HttpServlet,并由此继承service()方法的一个实现,它取一个HttpServletRequest和一个HttpServletResponse作为参数。
- HttpServlet扩展了javax.servlet.GenericServlet,这是一个抽象类,实现了大多数基本servlet方法。
- GenericServlet实现了Servlet接口。
- Servlet相关的类都在以下两个包中 : javax.servlet 或 javax.servlet.http。
- 可以覆盖init()方法,而且必须覆盖一个服务方法。
复习:HTTP和HttpServletRequest
- HttpServlet的doGet()和doPost()方法去一个HttpServletRequest和一个HttpServletResponse作为参数。
- service()方法根据HTTP请求的HTTP方法(get、post等)来确定运行doGet()还是doPost()。
- POST请求由一个体;GET请求没有,不过GET请求可以把请求参数追加到请求URL的后面(又是称为“查询串”)。
- GET请求本质上讲(根据HTTP规范)是幂等的。它们应当能多次运行而不会对服务器产生任何副作用。GET请求不应该修改服务器上的任何东西。但是你也可以写一个非幂等的doGet()方法。
- POST本质上讲不是幂等的,所以要由你来适当地设计和编写代码,如果客户错误地把一个请求发送了两次,你也能正确地加以处理。
- 如果HTML表单没有明确的指出“method=”POST””,请求就会作为一个GET请求发送,而不是POST请求。如果你的servlet中没有doGet(),这个请求就会失败。
- 可以用getParameter(paramname)方法从请求得到参数,返回值是String。
- 如果对应一个给定的参数名有多个参数值,要使用getParameterValues(paramname)方法来返回一个String数组。
- 从请求对象还可以得到其他东西,包括首部、cookie、会话、查询串和输入流。
HttpServletResponse响应的输出流
有两种:
- PrintWriter对象,用于输出字符。
- OutputStream对象,用于输出字节。
内容类型
//响应一个应用或者jar包给客户
response.setContentType("application/jar");
设置响应首部和增加 响应首部
setHeader(String,String) 会覆盖现有值
setIntHeader(String,Integer) 会覆盖现有值
addHeader(String,String) 会增加另外一个值
重定向与请求分派
重定向response.sendRedirect(String url)
,是返回给客户端,响应状态码会是301。
请求分派request.getRequestDispatcher(String url).forward(request,response)
,这里的url是发送给服务器,而不是客户端,响应状态码为200。
表示为表格形式:
方法 | 返回对象 | 状态码 |
---|---|---|
重定向 | 客户端 | 301 |
请求分派 | 服务器 | 200 |
复习:HttpServletResponse
- 使用响应向客户发回数据。
- 对响应对象(HttpServletResponse)调用的最常用的方法是setContentType()和getWriter()。
- 要当心——很多开发人员都认为应该是getPrintWriter(),但实际上得到书写器的方法是getWriter()。
- 利用getWriter()方法可以完成I/O,向流写入HTML(或其他内容)。
- 还可以使用响应来设置首部、发送错误、以及增加cookie。
- 在实际中,可能会使用JSP发送大多数HTML响应,但仍有可能使用一个响应流向客户发送二进制数据。
- 要得到二进制流,需要在响应上调用getOutputStream()。
- setContentType()方法告诉浏览器如何处理随响应到来的数据。常见的内容类型为”text/html”、”application/pdf”和”image/jpeg”。
- 不用记住内容类型。
- 可以使用addHeader()或setHeader()设置响应首部。二者区别是这个首部是否已经是响应的一部分。如果是,setHeader()会替换原来的值,而addHeader()会向现有的响应增加另一个值。
- 如果你不想对一个请求做出响应,可以把请求重定向到另一个URL。浏览器会负责把新请求发送到你提供的URL。
- 要重定向一个请求,需要在响应上调用sendRedirect(String URL)。
- 不能再响应已经提交之后才调用sendRedirect()。
- 请求重定向与请求分派完全是两码事。请求分派在服务端发生,而重定向在客户端进行。请求分派把请求传递给服务器上的另一个组件(通常在同一个Web应用中)。请求重定向只是告诉浏览器去访问另一个URL。