轻装上阵,安卓工程师之路---day06(HTTP & Servlet)

01 HTTP协议入门

HTTP协议是Web客户端和Web服务端通信的规则,用于定义客户端与web服务器通迅的格式,它是一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的过程

HTTP协议分类二个版本:

1_HTTP1.0版本

  客户端请求服务器后,服务器响应信息后,立即断开,且只能请求和响应一个资源

  缺点:客户端创建连接不容易,需要消耗大量的时间和资源,这时服务器就只响应一个资源就断开,下次客户端再请求,又要创建创建新的连接

2_HTTP1.1版本

  客户端请求服务器后,服务器响应信息后,不会立即断开,且在一定时间可可请求和响应多个资源

  优点:客户端请求服务器后,得到了响应,在一定时间内,这个连接还是保留的,

        在规则的时间内,客房端再次访问服务器,就不需要创建新的连接了

目前,Web应用都采用HTTP1.1协议规则,例如:GET(请求方式) /(请求资源) HTTP/1.1(协议名和版本)

开发最佳建议:在开发中要尽量减少客户端向WEB服务器发送HTTP请求数量,借此来提高客户端访问速度

面试题:

一个web页面中,使用img标签引用了三幅图片,当客户端访问服务器中的这个web页面时,客户端总共会访问几次服务器,即向服务器发送了几次HTTP请求。

通过软件的观察,总共有发送了四次请求,1次为文本请求,3次为图片请求。

体验:

1_IE浏览器去访问www.baidu.com回车

2_HttpWatch软件观察本次回车,浏览器共发送了多少个HTTP请求

02 HTTP请求

一个完整的HTTP请求,分为三部份:

1_请求行

  请求方式 请求内容 协议名和版本

不管POSTGET,都用于向服务器请求某个WEB资源,这两种方式的区别主要表现在数据传递上,客户端通过这两种方式都可以带一些数据给服务器:

如请求方式为GET方式,则可以在请求的URL地址后以?的形式带上交给服务器的数据,多个数据之间以&进行分隔,例如:

GET /mail/1.html?name=abc&password=xyz HTTP/1.1

GET方式的特点:在URL地址后附带的参数是有限制的,其数据容量不能超过1K

如请求方式为POST方式,则可以在请求的实体内容中向服务器发送数据,例如:

POST /servlet/ParamsServlet HTTP/1.1

Host:

Content-Type: application/x-www-form-urlencoded

Content-Length: 28

name=abc&password=xyz

Post方式的特点:传送的数据量无限制,如文件上传

2_请求头

  有很多key:value键值对组成的内容,每次请求都不相同

  

Accept: text/html,image/*  浏览器可以接收什么类型的响应数据  

Accept-Charset: GBK 浏览器可以接收什么类型的编码方式

Accept-Encoding: gzip,compress 浏览器可以接收gzipcompress格式的压缩数据,即浏览器收到这些压缩数据后会自动解压

Accept-Language: en-us,zh-cn 浏览器可以接收什么类型的语言

Host: www.itheima.com:80 浏览器请求的主机是www.itheima.com,使用80端口号

If-Modified-Since: Tue, 11 Jul 2014 18:23:51 GMT(缓存时间浏览器收到数据后,可缓存时间

Referer: http://www.itheima.com/index.html(来自哪请求来源于哪个URL地址

User-Agent:Mozilla/4.0 (compatible; MSIE 9; Windows NT 5.0) 浏览器的类型和版本

Cookie:后面细讲 浏览器缓存服务器响应的信息

Connection: close/Keep-Alive 浏览器与服务器的连接是打开还是关闭   

Date: Tue, 11 Jul 2014 18:23:51 GMT(访问时间浏览器请求服务器的时间,这个时间不是中国时间

3_请求体或实体内容

  请求头与请求内容有一个空行的间隔

  不是每次请求都有内容的,

  GET方式请求体是没有内容的,因为内容都在请求行中

  POST方式请求体是有内容的,例如:文件上传,表单以POST方式提交

03 HTTP响应

一个完整的HTTP响应,分为三部份:

响应行用于描述服务器,对请求的处理结果。

响应头用于描述服务器的基本信息,以及数据的描述,服务器通过这些数据的描述信息,可

以通知客户端如何处理等一会儿它回送的数据

实体内容,代表服务器向客户端回送的数据

1_响应行

 协议名和版本 响应状态码 响应状态码的英文描述

2_响应头

有很多key:value组成的内容,每次响应都不相同

Location: http://www.itheima/index.html 服务器要求浏览器访问的URL地址

Server:apache tomcat 服务器通知浏览器服务器的名字

Content-Encoding: gzip 服务器通知浏览器需要接收的压缩数据类型

Content-Length: 80 服务器通知浏览器需要接收的响应内容的字节数

Content-Language: zh-cn 服务器通知浏览器需要接收的语言类型

Content-Type: text/html; charset=GBK 服务器通知浏览器需要接收的类型和使用什么方式解码

Last-Modified: Tue, 11 Jul 2014 18:23:51 GMT 服务器通知浏览器浏览器访问的请求最近一次修改的时间

Refresh: 1; url=http://www.itheima.com 服务器通知浏览器1秒钟后刷新,并且访问指定的URL页面

content-Disposition: attachment; filename=aaa.zip(下载文件服务器通知浏览器以下载方式打开资源

Transfer-Encoding: chunked(分块传递数据到客户端)服务器通知浏览器以分块方式下载文件的浏览器指定的目录   

Set-Cookie:SS=Q0=5Lb_nQ; path=/search 服务器通知浏览器需要接收数据缓存到浏览器

Expires: -1//3种禁止缓存的头字段 以下三个响应头都表示服务器要求浏览器不要缓存来自服务器的web页面

Cache-Control: no-cache  

Pragma: no-cache   

Connection: close/Keep-Alive 服务器通知浏览器之间的连接是否已以关闭或者打开的  

Date: Tue, 11 Jul 2014 18:23:51 GM 服务器通知浏览器的时间

如果上述头中,请求也有,响应也用,我们叫其通用头,ConnectionDate就是通用头

3_响应体或实体内容

响应体和响应头之间有一个空行  

实体内容包含各种数据,音乐,文件,网页等等

响应状态码是服务器对这次响应设置的一人唯一编号,

每个编码都有其特定的含义,常见的响应编号有:

200:表示服务器响应正确

302:客户端请求一台服务端的资源,该服务端并没有这个资源,服务器要求客户端自已去另一台服务端找资源,这种情况下叫做重定向

307:客户端请求一台服务端的资源,该服务端并没有这个资源,服务器自已去另一台服务端找资源,这种情况下叫做转发

304:客户端请求服务端的资源,服务器没有修改过,且已经缓存到了客户端,要求客户端去其缓存中获取即可

404:客户端请求服务端的URL出错了

405: 服务端无法找到该servletdoGET方法或doPOST()方法,此错误一般都是方法确实引起的

500:客户端请求服务端的URL正确,但服务器处理资源出错了

04 Servlet入门

JavaEE系统结构分为三大块

1_客户层

2_JavaEE服务层

  2_1_Web/表现层,例如:Servlet&Jsp

  2_2_商业逻辑层

3_EIS企业信息系统层,例如:数据库,第三方系统

什么是Servlet

A servlet is a small Java program that runs within a Web server. Servlets receive and respond to requests from Web clients, usually across HTTP, the HyperText Transfer Protocol. 

Servlet是运行于Web服务器中的一个特殊的Java应用程序,Servlet能够接收来自每个客户端的请求,并做之响应,双方遵循HTTP协议

Servlet将来在企业中能做哪些工作呢?

1_能够接收客户端HTTP请求,并做以不同的响应,即动态响应

2_能做一些需要动态显示的Web资源内容,例如:下载,上传,权控显示,用户注册,用户登录。。。

总之,Servlet中做动态Web应用的核心技术之一

第一个Servlet开发步骤:

1_创建一个Demo01A类,实现Servlet接口,重写service()方法

public class Demo01A implements Servlet {

public void destroy() {

}

public ServletConfig getServletConfig() {

return null;

}

public String getServletInfo() {

return null;

}

public void init(ServletConfig arg0) throws ServletException {

}

public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {

//向浏览器输出一短英文字符串

//从响应对象的获取输出流

//who访问me, 响应对应就指向who

//即然响应对象指向who,那么从响应对应中获取的输出流,自然指向who

PrintWriter pw = res.getWriter();

pw.write("<font style='color:red;font-size:111px'>HAHA</font>");

}

}

2_/WEB-INF/web.xml文件中配置上述Servlet相关的信息,例用外界访问

<servlet>

<servlet-name>Demo01A</servlet-name>

<servlet-class>cn.itcast.android.servlet.Demo01A</servlet-class>

</servlet>

<servlet-mapping>

<servlet-name>Demo01A</servlet-name>

<url-pattern>/Demo01A</url-pattern>

</servlet-mapping>

3_部署该web应用到web服务器,通过浏览器访问,地址如下:

  http://127.0.0.1:8080/itcast-day06_http_servlet/Demo01A回车

创建Servlet方式有三种

1_普通类实现Servlet接口,并重写service()方法,每次请求服务器都调用service()方法,请求N次,调用Nservice()

  缺点:不必要的方法我们必须得实现  

2_普通类继承GenericServlet实,并重写service(),每次请求服务器都调用service()方法,请求N次,调用Nservice()

  缺点:不能够区分不同的请求方式

3_普通类继承HttpServlet,并重写doGet()doPost(),每次请求服务器都调用doXxx()方法,请求N次,调用NdoXxx()

ctrl+shift+F格式化代码

ServletRequest是父接口,既能处理http协议,也能处理非http协议

HttpServletRequest是子接口,只能处理Http协议

web中,通常是http协议,所以我们建议用HttpServletRequest

05 Servlet生命周期

生命周期:Servletweb服务器中,从出现到消失到整个过程

为什么在Web中,要基于接口编程呢?

如果你基于tomcat服务器中的具体类来编程的话,那么将来你的Servlet只能部署到tomcat服务器中才能运行部署到其它web服务器,例如:weblogic的话,就失败了;返之,我基于原SUN公司提供的接口编程,实现类都是由不同web服务器来完成,那么将来我的Servlet即可以在tomcat中运行成功,也可以在其它web服务器中运行成功,这样我的程序可移植性就强了

无参构造器()   

通过调用的无参构造获取字节码文件的Class对象,通过反射newIntance构造出一个新的对象,该对象只会构造一次。

init()

含有servletconfig对象,可以调用xml文件的初始化参数

doGet/Post() 

属于service来调拨的方法

destory()

服务器正常关闭时会执行,或者重新启动时也会执行,一个servlet生命周期中只会执行一次

默认情况下第一次访问会依次执行:无参构造器()->init()->doGet()/Post()---都只执行一次

第二次,第N次执行:doGet()/Post()---都只执行多次

重新部署时:destory()---都只执行一次

始终只有一个Servlet为所有客户端服务,即Servlet是单例的

非单列又叫多例

06 ServletConfig对象祥解

servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servletinit方法时,将ServletConfig对象传递给servlet。进而,程序员通过ServletConfig对象就可以得到当前servlet的初始化参数信息。

通过ServletConfig读取web.xml文件中为该Servlet配置的初始化参数

<servlet>

<servlet-name>Demo06</servlet-name>

<servlet-class>cn.itcast.android.servlet.Demo06</servlet-class>

<init-param>

<!-- Servlet初始化参数名 -->

<param-name>email</param-name>

<!-- Servlet初始化参数值 -->

<param-value>runsin0723@163.com</param-value>

</init-param>

<init-param>

<param-name>location</param-name>

<param-value>东圃</param-value>

</init-param>

</servlet>

ServletConfig API,并举例说明该对象的作用:

getInitParameterNames();

getInitParameter();

getServletName();

07 配置Servlet自动加载

在默认情况下,Servlet必须要首次访问时,方可创建并初始化

那有没有方式将Servlet的创建和初始化提前到web服务器启动时呢?

通常<load-on-startup>配置>=0的整数,如果配置<0的数和没配一样,即在首次访问Servlet时创建和初始化

当有多个Servlet时,数值越小,越先创建和初始化

    <servlet>

     <servlet-name>Demo07A</servlet-name>

     <servlet-class>cn.itcast.android.servlet.Demo07A</servlet-class>

     <!-- 配置Servlet自动加载 -->

     <load-on-startup>1</load-on-startup>

   </servlet>

08 提倡创建Servlet直接继承HttpServlet

因为:

1_我们在企业中,处理的web请求,大多是HTTP协议的,正好HttpServlet类是符合HTTP协议规则的

2_HttpServlet类,继承了GenericServlet类,也实现了Servlet接口,不光有祖先的功能,也有自已独特的操作

3_HttpServlet类可以根据客户端提交的方法来处理,例如:get请求,doGet()方法,通常不用去重写service()方法

4_虽说HttpServlet类无init()init(ServletConfig)方法,我们也可以获取web.xml文件中对servlet配置的初始化参数

  this.getServletConfig()方法来获取ServletConfig对象

09 Servlet的运行过程

参见图解

注意:客户端不能直接请求Servlet

      Servlet也不能直接响应客户端,中间必须经过Web服务器或容器

Servlet接口定义了Servlet生命周期

init()方法

服务器调用该方法初始化Servlet

service()方法

初始化完毕,服务器调用该方法响应客户的请求

destroy()方法

服务器调用该方法消灭servlet对象

注意:init()方法只在Servlet第一次被请求加载的时候被调用一次,当有客户再请求Servlet服务时,Web服务器将启动一个新的线程,在该线程中,调用service方法响应客户的请求整个过程,Servlet只创建了一次,即单例模式

10 配置Servlet虚拟路径

配置Servlet映射路径有二种类型的格式:

1_   /*

     表示访问当前web应用下的所有资源都由同一个Servlet来处理

2_   *.do(do可以换成其它)

<servlet-mapping>

<servlet-name>Demo08</servlet-name>

<url-pattern>/*</url-pattern>

<url-pattern>*.do</url-pattern>

</servlet-mapping>

Servlet映射到的URL中也可以使用*通配符,但是只能有两种固定的格式:一种格式是“*.扩展名”,另一种格式是以正斜杠(/)开头并以“/*”结尾。

总结:

1、只能使用 或 开头

2和 *. 不能同时存在,即/*.do绝对会报错;

3或 /* 表示任意

4*表示通配符

5.  /* 的优先级大于 *.do

[包含符号:/a-z*.]

11 缺省Servlet

项目的,将<url-pattern>/</url-pattern>Servlet,叫缺省Servlet

缺省Servlet通常做一些其它Servlet不处理的HTTP请求

缺省Servlet程序员不用去编写,因为每个web服务器中都由缺省Servlet,

例如:404页面就是由缺省Servlet来输出的

如果有二个缺省Servlet,只要访问的是当前web应用,就由当前web应用的缺省Servlet处理;

如果访问的不是当前web应用,则由tomcat中的缺省Servlet处理

所在当前web应用可以没有缺省Servlet,但tomcat必须有缺省Servlet

开发工具中的src目录,部署到tomcat中,就变成了/WEB-INF/classes目录

关于缺省Servlet

如果某个Servlet的映射路径仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用程序的缺省Servlet。 

凡是在web.xml文件中找不到匹配的<servlet-mapping>元素的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理所有其他Servlet都不处理的访问请求。Tomcat默认存在一个缺省Servlet,报错的404页面就是例子,单若项目中含有缺省值文件,就会优先执行项目中的缺省servlet,忽视Tomcat默认处理界面。

12.配置Servlet自动加载

如果在<servlet>元素中配置了一个<load-on-startup>元素,那么WEB应用程序在启动时,就会装载并创建Servlet的实例对象、以及调用Servlet实例对象的init()方法。正整数有效,数字越低,优先级越高。

Day06 重点代码

HTTP部分:

HTTP请求与响应练习

/**

 * 禁止浏览器缓存Demo01这个web页面

 */

具体类

public class Demo01 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

//禁止浏览器缓存Demo01这个web页面,因为每个浏览器不同,要针对所有浏览器进行设置

WebUtil.setIeNoCache(response);

//服务器向浏览器输出中文,text/html;charset=UTF-8就是响应头

response.setContentType("text/html;charset=UTF-8");

//以字符流方式输出

response.getWriter().write("<font style='font-size:111px;color:red'>哈哈</font>");

}

}

/**

 * 工具类

 */

public class WebUtil {

/**

 * 不要外界创建

 */

private WebUtil(){}

/**

 * 禁止浏览器缓存web页面

 */

public static void setIeNoCache(HttpServletResponse response){

//禁止浏览器缓存Demo01这个web页面,因为每个浏览器不同,要针对所有浏览器进行设置

response.setDateHeader("expires",-1);

response.setHeader("cache-control","no-cache");

response.setHeader("pragma","no-cache");

}

}

---------------------------------------------------------------------------------------------------------------------------------------

/**

 * 通知浏览器3秒后跳到url指定的web页面

 */

public class Demo02 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

response.setHeader("Refresh","3;url=http://127.0.0.1:8080");

}

}

---------------------------------------------------------------------------------------------------------------------------------------

/**

 * 下载文件

 */

public class Demo03 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

//响应头

//content-disposition表示下载响应头 

//attachment表示浏览器会打开一个下载提示框

//filename=abc.jpg表示浏览器打开的下载提示框中显示的下载文件名

//今天下载的文件是假的

//如果是中文文件名的话,一定要进行URL编码,各个浏览器做URL解码

String filename = "中文.jpg";

filename = URLEncoder.encode(filename,"UTF-8");

response.setHeader("content-disposition","attachment;filename="+filename);

}

}

---------------------------------------------------------------------------------------------------------------------------------------

编写Servlet的三种方式:

方法一:实现Servlet接口

public class Demo01A implements Servlet {

public void destroy() {

}

public ServletConfig getServletConfig() {

return null;

}

public String getServletInfo() {

return null;

}

public void init(ServletConfig arg0) throws ServletException {

}

public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {

//向浏览器输出一短英文字符串

//从响应对象的获取输出流

//who访问me, 响应对应就指向who

//即然响应对象指向who,那么从响应对应中获取的输出流,自然指向who

PrintWriter pw = res.getWriter();

pw.write("<font style='color:red;font-size:111px'>HAHA</font>");

}

}

方法二:继承GenericServlet抽象类,注意,GenericServlet该抽象类是一个空实现,仅仅为了快速编写servletservice方法

/**

 * 创建Servlet程序的方式二

 * GenericServlet类已对Servlet接口的中的所有方法做了空实现,即方法体无内容

 */

public class Demo01B extends GenericServlet {

public Demo01B(){

System.out.println("创建Demo01B");

}

@Override

public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException {

//通知浏览器需要接收一个html页面,其中字符串用UTF-8的方式进行解码

res.setContentType("text/html;charset=utf-8");

//获取指向每个浏览器的字符输出流

PrintWriter pw = res.getWriter();

//向浏览器输出一段中文字符串

pw.write("<font style='font-size:111px;color:red'>Servlet是做动态Web技术之一</font>");

}

}

方法三:推荐使用,继承HTTPServlet类,该类是GenericServlet类的具体实现,service方法不用覆盖,作为中转方法,转到doGETdoPOST方法上

/**

 * 创建Servlet程序的方式三

 */

public class Demo01C extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

System.out.println(request);

System.out.println(response);

//通知浏览器接收类型和解码方式

response.setContentType("text/html;charset=UTF-8");

//从响应对应的中获取字符输出流

PrintWriter pw = response.getWriter();

//以下代码输出一个登录表单

pw.write("<form action='#' method='POST'>");

pw.write("<table border='2' align='center'>");

pw.write("<caption><h2>用户登录</h2></caption>");

pw.write("<tr><th>姓名</th><td><input type='text' name='username'/></td></tr>");

pw.write("<tr><th>密码</th><td><input type='password' name='password'/></td></tr>");

pw.write("<tr><td colspan='2' align='center'><input type='submit' value='提交'/></td></tr>");

pw.write("</table>");

pw.write("</form>");

}

}

---------------------------------------------------------------------------------------------------------------------------------------

/**

 * 演示Servlet生命周期中各方法的执行情况 通过多次的点击,发现除了首次执行空参构造与init方法外,其余点击都是执行service,而且地址值得hascode都相同,所以证明servlet是单例模式

 *每次执行访问都创建新的httpservletrequest对象与httpservletreaonse对象

 */

public class Demo05 extends HttpServlet {

public Demo05() {

System.out.println("Demo05()::" + this.hashCode());

}

public void init() throws ServletException {

System.out.println("init()::" + this.hashCode());

}

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

System.out.println("doGet()::" + this.hashCode());

}

public void destroy() {

System.out.println("destroy()::" + this.hashCode());

}

}

---------------------------------------------------------------------------------------------------------------------------------------

init初始化方法能获取在servlet加载时设定在web.xml的初始化对象,ServletConfig类对象还有很多好方法,请查看API

/**

 * 通过init(ServletConfig对象)方法获取web.xml文件中Servlet配置的初始化参数

 * 单例

 */

public class Demo06 extends HttpServlet {

/**

 * ServletConfig对象做成实例变量

 * 被所有客户端共享,只能做读操作,如果做写操作的话,非常危险

 */

private ServletConfig config;

public Demo06(){

System.out.println("Demo06()");

}

/**

 * 读取web.xml文件中的servlet初始化参数

 */

@Override

public void init(ServletConfig config) throws ServletException {

//为实例变量ServletConfig设置值

this.config = config;

}

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

response.setContentType("text/html;charset=UTF-8");

PrintWriter pw = response.getWriter();

//一次性获取servlet的所有初始化参数名,初始化参数存放于web.xml文件中

Enumeration<String> enums = config.getInitParameterNames();

//迭代

while(enums.hasMoreElements()){

//获取初始化参数名,例如:emaillocation

String name = enums.nextElement();

//根据实始化参数名获取对应的参数值,例如:"xxxx""yyyy"

String value = config.getInitParameter(name);

//在浏览器在显示

pw.write(name+":"+value+"<br/>");

}

//获取Servlet名字,即在web.xml文件中<servlet-name>

pw.write(config.getServletName()+"<br/>");

}

}

---------------------------------------------------------------------------------------------------------------------------------------

load-on-startup:正整数,数字越小优先级越高,0的优先级最高

<servlet>

<servlet-name>Demo07A</servlet-name>

<servlet-class>cn.itcast.android.servlet.Demo07A</servlet-class>

<!-- 配置Servlet自动加载 -->

<load-on-startup>1</load-on-startup>

</servlet>

<servlet>

<servlet-name>Demo07B</servlet-name>

<servlet-class>cn.itcast.android.servlet.Demo07B</servlet-class>

<load-on-startup>2</load-on-startup>

</servlet>

---------------------------------------------------------------------------------------------------------------------------------------

/**

 * 写一个Servlet,用于读取E:/下的图片,响应给所有浏览器

 * 注意:文件若不是文本文件,只能采用字节流方式读取,不然会乱码,打不开

 */

public class Demo09 extends HttpServlet {

public void doGet(HttpServletRequest request, HttpServletResponse response)throws ServletException, IOException {

System.out.println("Demo09");

//字节输入流,路径要看tomcat中,不是看开发工具中

InputStream is = this.getClass().getClassLoader().getResourceAsStream("../../images/广州地铁规划图.jpg");

//字节输入流

OutputStream os = response.getOutputStream();

//以下是通过循环读取文件内容,并输出到浏览器中

byte[] buf = new byte[1024];

int len = 0;

while((len=is.read(buf))>0){

os.write(buf,0,len);

}

os.close();

is.close();

}

}

原文地址:https://www.cnblogs.com/canceler/p/4681613.html