JavaWeb——Servlet
一、基本概念
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的更多相关文章
- JavaWeb—Servlet
1.什么是Servlet 用来扩展web服务器功能的组件——早期的web服务器只能处理静态资源的请求,即需要事先将html文件准备好,并存放到web服务器上面.不能够处理动态资源的请求(需要计算,动态 ...
- JavaWeb:Servlet技术
JavaWeb:Servlet技术 快速开始 Servlet是什么 Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 ...
- [JavaWeb] Servlet Filter
作用: Servlet 过滤器可以动态地拦截请求和响应,以变换或使用包含在请求或响应中的信息. 可以将一个或多个 Servlet 过滤器附加到一个 Servlet 或一组 Servlet.Servle ...
- 【Java123】JavaWeb Servlet开发
http://www.runoob.com/servlet/servlet-intro.html https://www.cnblogs.com/xdp-gacl/tag/JavaWeb学习总结/de ...
- JavaWeb——Servlet开发
什么是Servlet? Servlet运行的过程 Servlet的生命周期 生命周期的各个阶段 Servlet的配置 使用Web.xml配置 使用注解配置 Servlet相关接口 ServletCon ...
- javaweb——Servlet作为控制器
1.文件预览 JSP:login.jsp--用户登陆,jsp只是用于向客户端展示信息,处理信息的是servlet. welcome.jsp--用户登陆成功,显示给用户的界面. jsp文件是位于webC ...
- javaweb servlet中使用请求转发乱码
乱码的方式有很多,这里指出一种不容易想到的 *请确保您的页面单独访问正常,经过servlet请求转发时,有PrintWriter out = response.getWriter()不正常,没有正常 ...
- Javaweb Servlet出现Class xxx is not a servlet错误原因
- javaWeb+servlet+mysql实现简单的企业员工管理系统
企业员工信息管理系统 一.源码描述 本程序为企业员工信息管理系统.是javaEE一个系统,主要实现登录功能和两个模块信息的增删改查.可以作为JAVAweb学习,也可在原有基础上进行深一步的 ...
随机推荐
- java基础集合经典训练题
第一题:要求产生10个随机的字符串,每一个字符串互相不重复,每一个字符串中组成的字符(a-zA-Z0-9)也不相同,每个字符串长度为10; 分析:*1.看到这个题目,或许你脑海中会想到很多方法,比如判 ...
- LDR详解
ARM指令集中,LDR通常都是作加载指令的,但是它也可以作伪指令. LDR伪指令的形式是"LDR Rn,=expr".下面举一个例子来说明它的用法. COUNT EQU ...
- sublime常用快捷键
自己觉得比较实用的sublime快捷键: Ctrl + / ---------------------注释 Ctrl + 滚动 --------------字体变大/缩小 Ctrl + N----- ...
- spring源码分析之context
重点类: 1.ApplicationContext是核心接口,它为一个应用提供了环境配置.当应用在运行时ApplicationContext是只读的,但你可以在该接口的实现中来支持reload功能. ...
- SQL Server2016升级前几点自检
SQL Server2016已经出来一段时间了,而且最新的SP1包也于2016年11月18日正式发布,各种新的特性推出让我们跃跃欲试.那么对于我们真实的业务环境,特别是生产环境要不要"跟风& ...
- Response.Redirect引起的性能问题分析
现象: 最近做的一个系统通过单点登录(SSO) 技术验证用户登录.用户在SSO 系统上通过验证后,跳转到该系统的不同模块.而跳转的时间一直维持子啊几分钟左右. 分析步骤: 在问题复现时抓取Hang d ...
- C# salt+hash 加密
一.先明确几个基本概念 1.伪随机数:pseudo-random number generators ,简称为:PRNGs,是计算机利用一定的算法来产生的.伪随机数并不是假随机 数,这里的" ...
- VS2015 Git 源码管理工具简单入门
1.VS Git插件 1.1 环境 VS2015+GitLab 1.2 Git操作过程图解 1.3 常见名词解释 拉取(Pull):将远程版本库合并到本地版本库,相当于(Fetch+Meger) 获取 ...
- python性能检测工具整理
python 运行后出现core dump产生core.**文件,可通过gdb来调试 Using GDB with a core dump having found build/python/core ...
- MyEclipse对Maven的安装
好记性不如烂笔头,记录一下. 操作系统:windows 7 MyEclipse2015 JDK1.7 maven的下载链接,点这里下载apache-maven-3.0.4-bin.tar.gz. 下载 ...