一、基本概念

Servlet是运行在Web服务器上的小程序,通过http协议和客户端进行交互。

这里的客户端一般为浏览器,发送http请求(request)给服务器(如Tomcat)。服务器接收到请求后选择相应的Servlet进行处理,并给出响应(response)。

从这里可以看出Servlet并不是独立运行的程序,而是以服务器为宿主,由服务器进行调度的。通常我们把能够运行Servlet的服务器称作Servlet容器,如Tomcat。

这里Tomcat为什么能够根据客户端的请求去选择相应的Servlet去执行的呢?答案是:Servlet规范。因为Servlet和Servlet容器都是遵照Servlet规范去开发的。简单点说:我们要写一个Servlet,就需要直接或间接实现javax.servlet.Servlet。并且在web.xml中进行相应的配置。Tomcat在接收到客户端的请求时,会根据web.xml里面的配置去加载、初始化对应的Servlet实例。这个就是规范,就是双方约定好的。

二、样例分析

在进一步解释Servlet原理、分析源码之前,我们先介绍下如何在JavaWeb中使用Servlet。方法很简单:1.编写自己的Servlet类,这里可以使用开发工具(STS、Myeclipse等)根据向导快速的生成一个Servlet类。2.在web.xml中配置servlet。这里的知识很简单,所以不做过多赘述。直接上代码。(这里需要注意的是,servlet3.0之后提供了注解WebServlet的方式配置servlet,这里就不做介绍了,感兴趣的可以自行去百度,只是配置的形式不同而已,没有本质区别。所以下文还是为web.xml为例)

TestServlet.java

 public class TestServlet extends HttpServlet {
private static final long serialVersionUID = 1L; public TestServlet() {
} protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
response.getWriter().append("Served at: ").append(request.getContextPath());
} protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}

web.xml

 <servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>com.nantang.servlet.TestServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/test</url-pattern>
</servlet-mapping>

启动Tomcat,浏览器访问/test。将会访问TestServlet。返回客户端请求的上下文路径。

aaarticlea/png;base64,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" alt="" />

这里需要扩展的有几点:

1.如果一个servlet需要映射多个url-pattern,那么就在<servlet-mapping></servlet-mapping>标签下写多个<url-pattern></url-pattern>,如:

 <servlet-mapping>
<servlet-name>TestServlet</servlet-name>
<url-pattern>/test1</url-pattern>
<url-pattern>/test2</url-pattern>
</servlet-mapping>

2.对于不同的servlet,不允许出现相同的url-pattern。

3.如果不同的servlet,它们的url-patter存在包含关系,那么容器会调用更具象的servlet去处理客户端请求。比如有两个servlet,servlet1的url-pattern是"/*",servlet2的url-pattern是"/test"。那么这个时候如果客户端调用的url是http://localhost:8080/demo/test,容器会使用servlet2去处理客户端的请求。虽然说"/*"和"/test"都匹配客户请求的url,但是容器会选择更贴切的。这里不会出现多个servlet处理同一个请求的现象。

三、源码分析

上面说过,我们自己编写的Servlet类都必须直接或间接实现javax.servlet.Servlet。可是上面的例子TestServlet继承的是HttpServlet,那是因为HttpServlet间接的实现了javax.servlet.Servlet。下面是HttpServlet的继承层级(类图中的方法并没有一一列举,因为下面会逐一解释):

下面我们由上往下层层分析:

1 ServletContext

一个web应用对应一个ServletContext实例,关于ServletContext的详细介绍,可以参考另一篇博文ServletContext

2.ServletConfig

ServletConfig实例是由servlet容器构造的,当需要初始化servlet的时候,容器根据web.xml中的配置以及运行时环境构造出ServletConfig实例,并通过回调servlet的init方法传递给servlet(这个方法后面会讲到)。所以一个servlet实例对应一个ServletConfig实例。

 public interface ServletConfig {

     public String getServletName();

     public ServletContext getServletContext();

     public String getInitParameter(String name);

     public Enumeration getInitParameterNames();
}

2.1 getServletName

getServletName方法返回servlet实例的名称,这个就是我们在web.xml中<servlet-name>标签中配置的名字,当然也可以在服务器控制台去配置。如果这两个地方都没有配置servlet名称,那么将会返回servlet的类名。

2.2 getServletContext

getServletContext方法返回ServletContext实例,也就是我们上面说的应用上下文。

2.3 getInitParameter和getInitParameterNames

这两个方法是用来获取servlet的初始化参数的,这个参数是在web.xml里面配置的(如下所示)。getInitParameter是根据参数名获取参数值,getInitParameterNames获取参数名集合。

这里需要注意的是当需要配置多个初始化参数时,应该写多个<init-param></init-param>对,而不是在一个<init-param></init-param>对里面写多个<param-name></param-name>和<param-value></param-value>对。

 <servlet>
<servlet-name>TestServlet</servlet-name>
<servlet-class>com.nantang.servlet.TestServlet</servlet-class>
<init-param>
<param-name>a</param-name>
<param-value>1</param-value>
</init-param>
<init-param>
<param-name>b</param-name>
<param-value>2</param-value>
</init-param>
</servlet>

3 Servlet

最原始最简单的JaveWeb模型,就是一个servlet容器上运行着若干个servlet用来处理客户端的请求。所以说servlet是JavaWeb最核心的东西,我们的业务逻辑基本上都是通过servlet实现的(虽然现在有各种框架,不用去直接编写servlet,但本质上还是在使用servlet)。

 public interface Servlet {

     public void init(ServletConfig config) throws ServletException;

     public ServletConfig getServletConfig();

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

     public String getServletInfo();

     public void destroy();
}

所有的servlet都是javax.servlet.Servlet的子类,就像Java里面所有的类都是Object的子类一样。Servlet类规定了每个servlet应该实现的方法,这个是遵循Servlet规范的。但是自定义的servlet一般不用直接实现Servlet,而是继承javax.servlet.GenericServlet或者javax.servlet.http.HttpServlet就行了。我们上面的TestServlet就是继承HttpServlet,这是因为HttpServlet间接实现了Servlet,提供了通用的功能。所以我们在自定义的TestServlet里面只需要专注实现业务逻辑就行了。

Servlet里面有三个比较重要的方法:init、service、destroy。它们被称作是servlet生命周期的方法,它们都是由servlet容器调用。另外两个方法用于获取servlet相关信息的,需要根据业务逻辑进行实现和调用。

3.1 init

init方法是servlet的初始化方法,当客户端第一次请求servlet的时候,JVM对servlet类进行加载和实例化。(如果需要容器启动时就初始化servlet,可以在web.xml配置<load-on-startup>1</load-on-startup>)

这里需要注意的是,servlet会先执行默认的构造函数,然后回调servlet实例的init方法,传入ServletConfig参数。这个参数上面说过,是servlet容器根据web.xml中的配置和运行时环境构造的实例。通过init方法注入到servlet。init方法在servlet的生命周期中只会被调用一次,在客户端的后续请求中将不会再调用。

3.2 service

service方法是处理业务逻辑的核心方法。当servlet容器接收到客户端的请求后,会根据web.xml中配置的<url-pattern>找到相应的servlet,回调service方法处理客户端的请求并给出响应。

3.3 destroy

JDK文档解释这个方法说:这个方法会在所有的线程的service()方法执行完成或者超时后执行。这里只是说明了,当servlet容器要去调用destroy方式的时候,需要等待一会,等待所有线程都执行完或者达到超时的限制。

这里并没有说清楚什么情况下servlet容器会触发这个动作。How Tomcat Works一书中对这个做了解释:当servlet容器关闭或需要更多内存的时候,会销毁servlet。这个方法就使得servlet容器拥有回收资源的能力。

同样地,destroy方法在servlet的生命周期中只会被调用一次。

3.4 getServletConfig

这个方法返回ServletConfig实例,这个对象即为servlet容器回调init方法的时候传入的实例。所以自定义的Servlet一般的实现方式为:在init方法里面把传入的ServletConfig存储到servlet的属性字段。在getServletConfig的实现里返回该实例。这个在后续解释javax.servlet.GenericServlet的源码时,能够看到。

3.5 getServletInfo

返回关于servlet的信息,这个由自定义的servlet自行实现,不过一般建议返回servlet的作者、版本号、版权等信息。

4.GenericServlet

GenericServlet从名字就能看的出来是servlet的一般实现,实现了servlet具有的通用功能,所以我们自定义的servlet一般不需要直接实现Servlet接口,只需要集成GenericServlet。GenericServlet实现了Servlet和ServletConfig接口。

4.1 GenericServlet对Servlet接口的实现

 private transient ServletConfig config;

 public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
} public void init() throws ServletException {} public abstract void service(ServletRequest req, ServletResponse res) throws ServletException, IOException; public void destroy() {} public ServletConfig getServletConfig() {
return config;
} public String getServletInfo() {
return "";
}

可以说,GenericServlet对Servlet方法的实现逻辑非常简单。就是把一些必要的逻辑写了下。

1.init方法就是把容器传入的ServletConfig实力存储在类的私有属性conifg里面,然后调用一个init无参的空方法。这么做的意义在于,我们如果想在自定义的servlet类里面在初始化的时候添加些业务逻辑,只需要重写无参的init方法就好了,我们不需要关注ServletConfig实例的存储细节了。

2.service和destroy方法并未实现具体逻辑。

3.getServletConfig就是返回init方法里面存储的config。getServletInfo就是返回空字符串,如果有业务需要,可以在子类里面重写。

4.2 GenericServlet对于ServletConfig接口的实现

 public String getServletName() {
ServletConfig sc = getServletConfig();
if (sc == null) {
throw new IllegalStateException(
lStrings.getString("err.servlet_config_not_initialized"));
}
return sc.getServletName();
} public ServletContext getServletContext() {
ServletConfig sc = getServletConfig();
if (sc == null) {
throw new IllegalStateException(
lStrings.getString("err.servlet_config_not_initialized"));
}
return sc.getServletContext();
} public String getInitParameter(String name) {
ServletConfig sc = getServletConfig();
if (sc == null) {
throw new IllegalStateException(
lStrings.getString("err.servlet_config_not_initialized"));
}
return sc.getInitParameter(name);
} public Enumeration getInitParameterNames() {
ServletConfig sc = getServletConfig();
if (sc == null) {
throw new IllegalStateException(
lStrings.getString("err.servlet_config_not_initialized"));
}
return sc.getInitParameterNames();
}

这四个方法的实现就跟一个模子刻出来的一样,都是取得ServletConfig实例,然后调用相应的方法。其实GenericServlet完全没有必要实现ServletConfig,这么做仅仅是为了方便。当我们集成GenericServlet写自己的servlet的时候,如果需要获取servlet的配置信息如初始化参数,就不需要写形如:“ServletConfig sc = getServletConfig();if (sc == null) ...;return sc.getInitParameterNames();”这些冗余代码了。除此之外,没有别的意义。

5 HttpServlet

HttpServlet是一个针对HTTP协议的通用实现,它实现了HTTP协议中的基本方法get、post等,通过重写service方法实现方法的分派。

 public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException{
HttpServletRequest request;
HttpServletResponse response;
try {
request = (HttpServletRequest) req;
response = (HttpServletResponse) res;
} catch (ClassCastException e) {
throw new ServletException("non-HTTP request or response");
}
service(request, response);
}

重写的service方法将参数转换成HttpServletRequest和HttpServletResponse,并调用自己的另一个重载service方法。

 protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
String method = req.getMethod();
if (method.equals(METHOD_GET)) {
long lastModified = getLastModified(req);
if (lastModified == -1) {
doGet(req, resp);
} else {
long ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);
if (ifModifiedSince < (lastModified / 1000 * 1000)) {
maybeSetLastModified(resp, lastModified);
doGet(req, resp);
} else {
resp.setStatus(HttpServletResponse.SC_NOT_MODIFIED);
}
}
} else if (method.equals(METHOD_HEAD)) {
long lastModified = getLastModified(req);
maybeSetLastModified(resp, lastModified);
doHead(req, resp);
} else if (method.equals(METHOD_POST)) {
doPost(req, resp);
} else if (method.equals(METHOD_PUT)) {
doPut(req, resp);
} else if (method.equals(METHOD_DELETE)) {
doDelete(req, resp);
} else if (method.equals(METHOD_OPTIONS)) {
doOptions(req,resp);
} else if (method.equals(METHOD_TRACE)) {
doTrace(req,resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[1];
errArgs[0] = method;
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED, errMsg);
}
}

这个方法的的逻辑也很简单,就是解析出客户端的request是哪种中方法,如果是get方法则调用doGet,如果是post则调用doPost等等。这样我们在继承HttpServlet的时候就无需重写service方法,我们可以根据自己的业务重写相应的方法。一般情况下我们的应用基本就是get和post调用。那么我们只需要重写doGet和doPost就行了。

这里需要注意的是3-15行代码,这里对资源(比如页面)的修改时间进行验证,判断客户端是否是第一次请求该资源,或者该资源是否被修改过。如果这两个条件有一个被满足那么就调用doGet方法。否则返回状态304(HttpServletResponse.SC_NOT_MODIFIED),这个状态就是告诉客户端(浏览器),可以只用自己上一次对该资源的缓存。

不过HttpServlet对于判断资源修改时间的逻辑非常简单粗暴:

 protected long getLastModified(HttpServletRequest req) {
return -1;
}

方法始终返回-1,这样就会导致每次都会调用doGet方法从服务器取资源而不会使用浏览器的本地缓存。所以如果我们自己的servlet要使用浏览器的缓存,降低服务器的压力,就需要重写getLastModified方法。

最后我们来看一下HttpServlet对http一些方法的实现,在所有的方法中,HttpServlet已经对doOptions和doTrace方法实现了通用的逻辑,所以我们一般不用重写这两个方法,感兴趣的可以自己去看下源码。

这里我们列举下最常用的两个方法doGet和doPost:

 protected void doGet(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_get_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
} protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException{
String protocol = req.getProtocol();
String msg = lStrings.getString("http.method_post_not_supported");
if (protocol.endsWith("1.1")) {
resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);
} else {
resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);
}
}

其实这两个实现里面也没做什么有用的逻辑,所以一般情况下都要重写这两个方法,就像我们最初的TestServlet那样。doHead、doPut、doDelete也是这样的代码模板,所以如果有业务需要的话,我们都要重写对应的方法。

四、总结

讲了这么多,可以这么说:Servlet是JavaWeb里面最核心的组件。只有对它完全融会贯通,才能去进一步去理解上层框架Struts、Spring等。

另外需要明确的是:一个Web应用对应一个ServletContext,一个Servlet对应一个ServletConfig。每个Servlet都是单例的,所以需要自己处理好并发的场景。

作者:南唐三少
出处:http://www.cnblogs.com/nantang

如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我们最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文链接,否则保留追究法律责任的权利。

JavaWeb——Servlet的更多相关文章

  1. JavaWeb—Servlet

    1.什么是Servlet 用来扩展web服务器功能的组件——早期的web服务器只能处理静态资源的请求,即需要事先将html文件准备好,并存放到web服务器上面.不能够处理动态资源的请求(需要计算,动态 ...

  2. JavaWeb:Servlet技术

    JavaWeb:Servlet技术 快速开始 Servlet是什么 Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 ...

  3. [JavaWeb] Servlet Filter

    作用: Servlet 过滤器可以动态地拦截请求和响应,以变换或使用包含在请求或响应中的信息. 可以将一个或多个 Servlet 过滤器附加到一个 Servlet 或一组 Servlet.Servle ...

  4. 【Java123】JavaWeb Servlet开发

    http://www.runoob.com/servlet/servlet-intro.html https://www.cnblogs.com/xdp-gacl/tag/JavaWeb学习总结/de ...

  5. JavaWeb——Servlet开发

    什么是Servlet? Servlet运行的过程 Servlet的生命周期 生命周期的各个阶段 Servlet的配置 使用Web.xml配置 使用注解配置 Servlet相关接口 ServletCon ...

  6. javaweb——Servlet作为控制器

    1.文件预览 JSP:login.jsp--用户登陆,jsp只是用于向客户端展示信息,处理信息的是servlet. welcome.jsp--用户登陆成功,显示给用户的界面. jsp文件是位于webC ...

  7. javaweb servlet中使用请求转发乱码

    乱码的方式有很多,这里指出一种不容易想到的 *请确保您的页面单独访问正常,经过servlet请求转发时,有PrintWriter out = response.getWriter()不正常,没有正常 ...

  8. Javaweb Servlet出现Class xxx is not a servlet错误原因

  9. javaWeb+servlet+mysql实现简单的企业员工管理系统

    企业员工信息管理系统 一.源码描述       本程序为企业员工信息管理系统.是javaEE一个系统,主要实现登录功能和两个模块信息的增删改查.可以作为JAVAweb学习,也可在原有基础上进行深一步的 ...

随机推荐

  1. UWP 律师查询 MVVM

    APP简介 律师查询是基于聚合数据的律师查询接口做的,这个接口目前处于停用状态,但是,由于我是之前申请的,所以,还可以用,应该是无法再申请了. 效果图 开发 一.HttpHelper 既然是请求接口的 ...

  2. eclipse 快捷键大全

    注:因eclipse版本.电脑配置等原因 有些快捷键可能导致不可用(遇到些许问题可在下方评论) [Ct rl+T] 搜索当前接口的实现类 1. [ALT +/]    此快捷键为用户编辑的好帮手,能为 ...

  3. PowerShell过滤文件中的重复内容

    Get-Content -Path E:\test11\data.txt | Sort-Object | Get-Unique 源文件: AA0001 2014-06-30 15:27:13.073 ...

  4. 6. ModelDriven拦截器、Preparable 拦截器

    1. 问题 Struts2 的 Action 我们将它定义为一个控制器,但是由于在 Action 中也可以来编写一些业务逻辑,也有人会在 Action 输入业务逻辑层. 但是在企业开发中,我们一般会将 ...

  5. await and async

    Most people have already heard about the new “async” and “await” functionality coming in Visual Stud ...

  6. CSS知识总结(七)

    CSS常用样式 5.背景样式 1)背景颜色 background-color : transparent | color 常用值:①英文单词,②十六进制,③RGB或RGBA 另外,还有一种是 渐变色彩 ...

  7. Hibernate中事务的隔离级别设置

    Hibernate中事务的隔离级别,如下方法分别为1/2/4/8. 在Hibernate配置文件中设置,设置代码如下

  8. Flex 布局教程:语法篇

    作者: 阮一峰 网页布局(layout)是CSS的一个重点应用. 布局的传统解决方案,基于盒状模型,依赖 display属性 + position属性 + float属性.它对于那些特殊布局非常不方便 ...

  9. ios label 自动计算行高详解

    在OC当中自动计算行高主要调用系统的 p.p1 { margin: 0.0px 0.0px 0.0px 0.0px; font: 11.0px Menlo; color: #ffffff } span ...

  10. iOS7 NavigationController 手势问题

    在iOS7中,如果使用了UINavigationController,那么系统自带的附加了一个从屏幕左边缘开始滑动可以实现pop的手势.但是,如果自定义了navigationItem的leftBarB ...