1. Servlet概述
1.1. Servlet概述
1.1.1. Servlet是什么
Servlet是sun公司提供的一门用于开发动态web资源的技术。
按照这套规范写出来的Servlet可以放置到web应用中在Servlet容器中运行。
1.1.2. 开发Servlet步骤
想要开发一个Servlet只需要两个步骤:
(1)写一个类实现javax.servlet接口。
(2)在web.xml中为servlet配置对外访问路径。
2. 开发第一个Servlet
2.1. 写一个类实现Servlet接口
2.1.1. Servlet接口api
接下来我们就手动编写一个Servlet感受一下Servlet开发的过程。
我们用记事本,写一个类实现Servlet接口,我们打开api发现如果直接实现Servlet接口需要实现如下方法,如图-1所示:
图-1
简单介绍下其中重要方法:
init(ServletConfig config)
初始化方法,会在Servlet被创建出来后立即执行,做一些初始化的工作
destroy()
销毁方法,会在Servlet被销毁之前执行,做一些善后工作
service(ServletRequest req, ServletResponse res)
服务方法,每当有请求访问Servlet时,此方法执行,处理请求的代码要写到这个方法里。
2.1.2. GenericServlet抽象类
我们发现这个接口中方法太多了,其实我们可以直接继承Servlet接口的一个默认实现了GenericServlet类,如图-2所示:
图-2
通过观察api,我们发现GenericServlet是个抽象类,实现了Servlet接口中的大部分方法,唯独service方法没有做实现,我们继承GenericServlet需要实现这个Service方法在其中写处理请求的代码。如图-3所示:
图-3
在记事本中编写如下代码,注意写的过程中需要导入包。如图-4所示:
图-4
我们输出当前时间到客户端,service方法有两个参数,ServletRequest代表HTTP请求,ServletResponse代表HTTP响应,我们想要获取客户端发送过来的信息时可以找ServletRequest,现在需要向客户端发送数据就可以使用ServletResponse对象。
通过查询ServletResponse对象的api,发现其中有获取写出数据的流的方法,通过这个方法获取流就可以将数据发送给浏览器。代码如图-5所示:
图-5
编写好java文件后,需要进行编译,如图-6所示:
图-6
在编译的过程中发现少了开发包,这是因为我们现在开发的是javaee项目,需要将javaee相关的开发包加入classpath环境变量,这个包在tomcat的支持包中存有,将其加入classpath环境变量即可。如图-7所示:
图-7
再次编译。
报出了警告,是因为Date的toLocaleString方法已经过时,但是我们不关心,到此编译已经完成。编译成功后将包拷入web应用的WEB-INF/classes目录下,如图-8所示:
图-8
2.2. 配置Servlet的对外访问路径
2.2.1. 在web.xml配置servlet
我们还需要在web.xml中为这个Servlet配置一个对外访问路径。
打开web.xml文件,在根标签下进行如下配置,如图-9所示:
图-9
其中,servlet-class中为配置的Servlet类的全路径名。
servlet-name是为该servlet配置的名称,此名称没有特殊要求,为了便于识别此处取名和类名相同。
url-pattern是为该名称的servlet配置对外访问路径,浏览器可以通过该路径访问此servlet。
启动服务器,通过浏览器访问,如图-10所示:
图-10
发现成功输出了当前时间,多次刷新页面发现每次显示的都是最新的时间,不同的人在不同的时间看到的结果不同,说明这确实是一个动态web资源。
3. Servlet的调用过程和生命周期
3.1. servlet的调用过程
3.1.1. Servlet调用过程图
当我们在访问这个Servlet时,是如何看到时间输出的呢?整个过程是如何工作的呢?我们画图解释,如图-11所示:
7
图-11
(1)在浏览器输入地址,浏览器先去查找hosts文件,将主机名翻译为ip地址,如果找不到就再去查询dns服务器将主机名翻译成ip地址。
(2)浏览器根据ip地址和端口号访问服务器,组织http请求信息发送给服务器。
(3)服务器收到请求后首先根据Host请求头判断当前访问的是哪台虚拟主机。
(4)服务器根据http请求头中的请求URI判断当前访问的是哪个web应用。
(5)服务器根据http请求头中的请求URI判断当前访问的是web应用中的哪个web资源。
(6)检查web应用的web.xml文件,如果根据路径找到具体的servlet处理类的全路径名交给该servlet处理,如果找不到就交给缺省servlet处理。
(7)这个过程中浏览器只知道自己发出来http请求,不久就收到了http响应,浏览器不知道也不关心服务器内部是如何处理的。浏览器和服务器之间的关系是非常单纯的,只有HTTP协议。
(8)解析请求、封装RequestResponse对象、创建Servlet、调用Service方法都是服务器自动进行的,开发人员只需要写好Servlet配置进容器中即可,无需操心具体的底层实现。---这就是容器啊!多重要!多形象!
3.2. servlet的生命周期
3.2.1. servlet生命周期详解
(1)Servlet第一次被访问到时创建对象,创建出来后立即执行init方法执行初始化的操作。
(2)从此以后该对象一直驻留在内存中为后续的对这个Servlet的请求进行服务。
(3)直到服务器关闭或web应用移除出容器时,随着web应用的销毁Servlet对象销毁掉,在销毁之前调用destory方法执行善后工作。
(4)在存活期间,每次对Servlet 的调用都会导致Service方法的执行。
4. 在myeclipse中开发Servlet
4.1. 为什么需要myeclipse
4.1.1. 为什么需要myeclipse
上面的例子中我们用记事本实现了一个Servlet,这样做的目的是为了让大家更好的理解Servlet的本质,但是如果在真实开发中也用记事本开发,可以想见效率一定是非常低的。接下来我们来了解一下如何在Myeclipse环境中开发Servlet
4.2. 在Myeclipse中开发Servlet
4.2.1. 第一步:创建servlet
在工程src目录上右键弹出菜单,选择new->Servlet,如图-12所示:
图-12
4.2.2. 第二步:配置servlet类信息
在弹出的对话框中输入Servlet的包名、类名,默认继承HttpServlet,覆盖其中doGet和doPost方法。如图-13所示:
图-13
4.2.3. 第三步:配置serlvet虚拟路径
进入下一界面,选择是否自动配置servlet到web.xml中,一旦勾选,则自动会用输入的信息在web.xml中为该Servlet配置对外访问路径,如图-14所示:
4.2.4. 第四步:编写servlet处理逻辑
点击确定,创建出Servlet,发现该类继承了HttpServlet(此类是Servlet接口的实现类,我们的类继承他,自然也是个Servlet),并且覆写了其中的doGet和doPost方法。
当客户端用get方式访问该Servlet时会导致doGet方法执行
当客户端用post方式访问该Servlet时会导致doPost方法执行
我们只需要写代码处理对应的处理逻辑即可。
很多时候我们的处理代码对于get方式的请求和post方式的请求的处理是相同的,此时可以在doPost中调用doGet();然后将处理代码写在doGet中。这样无论是get还是post请求都可以进行处理了。
编写代码,如图-15所示:
图-15
4.2.5. 第五步:将web应用发布到tomcat中
如图-16所示:
图-16
启动tomcat,如图-17所示:
图-17
4.2.6. 第六步:通过浏览器访问
由于web应用是发布到了webapps中即localhost虚拟主机中,所以按照如下方式访问,注意此处的/Demox不是工程名,而是发布时的指定的web应用名,如图-18所示:
图-18
通过浏览器访问,如图-19所示:
5. Servlet的继承结构
5.1. Serlvet继承结构
5.1.1. 概述
我们在手写Servlet时继承的是GenericServlet,而用Myeclipse生成的Servlet是继承了HttpServlet,那么他们之间的关系到底是什么样的呢?下面我们讨论下Servlet的继承结构:
5.1.2. Servlet接口
定义了一个servlet应该具有的方法,所有的Servlet都应该直接或间接实现此接口
5.1.3. GenericServlet抽象类
GenericServlet抽象类是对Servlet接口的默认实现,对Serlvet接口中的大部分方法都做了默认实现,只有service方法是一个抽象方法需要继承者自己实现。实现者只需要实现Service方法在其中写处理请求的代码即可。
5.1.4. HttpServlet类
继承自GenericServlet类,在GenericServlet类的基础上对HTTP协议进行了优化,并且实现了其中的service抽象方法,在其中判断了请求的请求方式,并根据请求方式的不同分别调用不同的doXXX()方法。
通常我们在开发Servlet时,直接继承HttpServlet覆盖对应的doGet()doPost()方法即可,一般不推荐直接覆盖service()方法。
6. Servlet对外访问路径配置细节
6.1. Servlet配置细节
6.1.1. 基本配置
Servlet需要在web.xml中配置对外访问的路径。如图-20所示:
图-20
其中:
<servlet>标签配置Servlet。
<servlet-mapping>标签配置该Servlet的对外访问路径。
一个<servlet>可以对应多个<servlet-mapping>。
6.1.2. 星号匹配符的使用
可以用*通配符配置<serlvet-mapping>,但是要注意,必须是 *.后缀 或者 /开头的以/*结尾的路径。
由于匹配符的引入有可能一个虚拟路径会对应多个<servlet-mapping>,此时的匹配优先原则为:哪个最像找哪个,*.后缀 优先级最低。
思考:
对于如下的一些映射关系:Servlet1 映射到 /abc/* Servlet2 映射到 /* Servlet3 映射到 /abc Servlet4 映射到 *.do 问题:当请求URL为“/abc/a.html”,“/abc/*”和“/*”都匹配,哪个servlet响应 Servlet引擎将调用Servlet1。当请求URL为“/abc”时,“/abc/*”和“/abc”都匹配,哪个servlet响应 Servlet引擎将调用Servlet3。当请求URL为“/abc/a.do”时,“/abc/*”和“*.do”都匹配,哪个servlet响应 Servlet引擎将调用Servlet1。当请求URL为“/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应 Servlet引擎将调用Servlet2。当请求URL为“/xxx/yyy/a.do”时,“/*”和“*.do”都匹配,哪个servlet响应 Servlet引擎将调用Servlet2。
6.1.3. 缺省Servlet
路径中有一个特殊的配置“/”,如果一个servlet的对外访问路径被设置为/,则该servlet就是一个缺省servlet,其他servlet不处理的请求都由它来处理。
在conf/web.xml(通用web.xml,参考tomcat章节)中配置了缺省servlet,对静态资源的访问和错误页面的输出就是由这个缺省servlet来处理的。
如果我们自己写一个缺省servlet把爸爸web.xml中的缺省servlet覆盖的话,会导致静态web资源无法访问。所以不推荐自己配置缺省Servlet。