Web开发之Servlet
目录
- `Servlet` 简述
- `Servlet` 接口
- `Servlet` 的生命周期和请求处理逻辑
- 使用 `Servlet` 开发示例
- 实现 `Servlet` 接口
- 继承 `GenericServlet` 抽象类
- 继承 `HttpServlet` 抽象类
- `HttpServlet` 抽象类的设计
- `Servlet` 细节
- `Servlet` 与单例
- 多个映射路径
- 映射路径通配符
- `Servlet` 映射路径优先级
- `JspServlet`
- 默认的 `Servlet`
- `Servlet` 初始化时机
Servlet
简述
Servlet
是运行在实现了 Java Servlet
规范的服务器端一个小程序,是 Java Web
的三大组件之一(Servlet
、Listener
监听器、Filter
过滤器),它属于动态资源,能够动态的根据请求的信息返回不同的响应
Servlet
是我们深入学习 SpringMVC
框架的基础
Servlet
接口
javax.serlvet.Servlet
接口是 Servlet
规范中最顶级的接口。此接口定义了初始化 servlet
、为请求提供服务以及从服务器中删除 servlet
的方法。这些方法称为 Servlet
的生命周期方法,它们按照一定顺序调用
package javax.servlet;
import java.io.IOException;public interface Servlet {/*** servlet 容器在实例化 servlet 后调用 init 方法一次进行初始化。只有完成该方法(不抛出异常),然后servlet才能接收任何请求* @param config 包含 servlet 的配置和初始化参数的 ServletConfig 对象*/public void init(ServletConfig config) throws ServletException;/*** 返回一个 ServletConfig 对象,该对象包含此servlet的初始化和启动参数,返回的ServletConfig对象是传递给init方法的参数对象* 此接口的实现负责存储ServletConfig对象,以便此方法可以返回它,实现此接口的 GenericServlet 类已经执行了此操作** @return 辅助初始化servlet的ServletConfig对象。*/public ServletConfig getServletConfig();/*** 由 servlet容器(Web 服务器)调用,以允许servlet处理和响应请求。只有在servlet的 init()方法成功完成后,才能调用此方法** @param req 包含客户端请求的 ServletRequest 对象* @param res 包含 servlet 响应的 ServletResponse 对象*/public void service(ServletRequest req, ServletResponse res)throws ServletException, IOException;/*** 返回有关 servlet 的信息,如作者、版本和版权。* 此方法返回的字符串为纯文本,而不是任何类型的标记(如 HTML、XML等)语言。** @return 返回有关 servlet 的信息字符串*/public String getServletInfo();/*** 由 servlet(Web 服务器)容器调用,以指示 servlet 正在停用* 当服务器正常关闭时会调用此方法,使servlet有机会清理正在保留的任何资源(例如, 内存、文件句柄、线程)* 在 servlet 容器调用此方法后,它将不会再在此 servlet 上调用service方法。*/public void destroy();
}
上面的 Servlet
实例和方法都是由 Servlet
容器(如 tomcat
服务器)调用的,因此我们的项目不需要任何 main
方法,也不需要创建任何 Servlet
实例
Servlet
的生命周期和请求处理逻辑
Web
服务器接收到HTTP
请求,将请求转发给servlet
容器,servlet
容器中根据请求路径查找对应的Servlet
- 如果
servlet
实例还未创建,那么就创建一个servlet
实例,随后servlet
容器调用servlet
的init()
方法对servlet
进行初始化(该方法只会在servlet
第一次被载入时调用)。被创建的servlet
实例会被保留在容器中,当后续有同样的请求到来时可以直接处理,不再需要重新创建和初始化 servlet
容器会解析到来的HTTP
请求,调用该servlet
实例的service()
方法,以执行service()
方法中的业务逻辑,service()
方法就是我们要重点开发的方法- 如果
Servlet
实例被销毁,那么将会由Servlet
容器执行destory()
方法,通常都是在服务器关闭时Servlet
才会销毁,destory()
方法可以进行资源的释放
使用 Servlet
开发示例
我们使用 Servlet
时,需要对 Servlet
进行实现。一般来说,实现方式有 3
种
- 实现
javax.servlet.Servlet
接口 - 继承
javax.servlet.GenericServlet
抽象类 - 继承
javax.servlet.http.HttpServlet
抽象类
实现 Servlet
接口
@WebServlet("/first-servlet")
public class FirstServlet implements Servlet {@Overridepublic void init(ServletConfig servletConfig) {System.out.println("---------------");System.out.println("servletConfig: " + servletConfig);System.out.println("init");}@Overridepublic ServletConfig getServletConfig() {System.out.println("---------------");System.out.println("getServletConfig");return null;}/*** 我们主要开发的方法** @param servletRequest 请求对象* @param servletResponse 响应对象*/@Overridepublic void service(ServletRequest servletRequest, ServletResponse servletResponse) throws IOException {//获取请求的数据String message = servletRequest.getParameter("xx");//封装响应数据servletResponse.setContentType("text/html");// HelloPrintWriter out = servletResponse.getWriter();out.println("<html><body>");out.println("<h1>" + message + "</h1>");out.println("</body></html>");}@Overridepublic String getServletInfo() {System.out.println("getServletInfo");return null;}@Overridepublic void destroy() {System.out.println("destroy");}
}
想要成功运行 servlet
还要进行 servlet
的路径配置。这里,我们使用 servlet 3.0
开始提供的 @WebServlet
注解来配置 servlet
的路径。当然也可以使用 web.xml
来配置,但是这比较麻烦。这里的路径必须使用 “/” 开头,并且相对的是当前应用根目录
如果是配置 web.xml
,那么可以这样配置
<servlet><!--3、 servlet的内部名称--><servlet-name>firstServlt</servlet-name><!--4、 servlet的类全路径名--><servlet-class>com.example.servlet_01.FirstServlet</servlet-class>
</servlet>
<!-- 映射 -->
<servlet-mapping><!--2、 servlet 的映射内部名称,通过他可以找到 servlet 的内部名称,不区分大小写--><servlet-name>firstServlt</servlet-name><!--1、 请求 servlet 的映射路径--><url-pattern>/first-servlet</url-pattern>
</servlet-mapping>
继承 GenericServlet
抽象类
java.servlet.GenericServlet
类使编写 servlet
变得更容易,它实现了 Servlet
和 ServletConfig
接口的方法(除了 service()
方法)。GenericServlet
类也实现了 log()
方法,这是一个在 ServletContext
接口中定义的方法,同时具有自己的 init()
和 log()
方法
Generic
意为通用的,自己编写的 servlet
直接扩展 GenericServlet
类比实现 Servlet
接口更加方便
@WebServlet("/generic-servlet")
public class MyGenericServlet extends GenericServlet {@Overridepublic void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {// 获取请求的数据String message = req.getParameter("xx");// 封装响应数据res.setContentType("text/html");// HelloPrintWriter out = res.getWriter();out.println("<html><body>");out.println("<h1>" + message + "</h1>");out.println("</body></html>");}
}
继承 HttpServlet
抽象类
实际上 GenericServlet
作为一个通用的 Servlet
实现,只提供了基本的功能,在某些情况下仍然不方便,比如我们的 Web
应用基本上都是基于 HTTP
协议的请求,因此非常有必要创造一个专门针对 HTTP
协议的 Servlet
,比如处理各种请求,获取请求头信息等等
Java
已经为我们提供了这样的一个 Servlet
实现,那就是javax.servlet.http.HttpServlet
。HTTP
的请求方式包括 DELETE、GET、OPTIONS、POST、PUT、TRACE
,在 HttpServlet
类中分别提供了相应的服务方法,它们是 doDelete()、doGet()、doOptions()、doPost()、doPut()、doTrace()
public abstract class HttpServlet extends GenericServlet {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);}}protected void doPut(HttpServletRequest req, HttpServletResponse resp)throws ServletException, IOException {String protocol = req.getProtocol();String msg = lStrings.getString("http.method_put_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 doDelete(HttpServletRequest req,HttpServletResponse resp)throws ServletException, IOException {String protocol = req.getProtocol();String msg = lStrings.getString("http.method_delete_not_supported");if (protocol.endsWith("1.1")) {resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, msg);} else {resp.sendError(HttpServletResponse.SC_BAD_REQUEST, msg);}}
}
当通过继承 HttpServlet
来创建一个 Servlet
时,我们只需要根据要处理的请求的类型,来重写不同的方法就可以了,处理 get
请求,则重写 doGet()
,处理 post
请求,则重写 doPost()
,无需实现其他方法,开发 Servlet
时更加简单
@WebServlet("/http-servlet")
public class MyHttpServlet extends HttpServlet {@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {// 获取请求的数据String message = req.getParameter("xx");// 封装响应数据resp.setContentType("text/html");// HelloPrintWriter out = resp.getWriter();out.println("<html><body>");out.println("<h1>" + message + "</h1>");out.println("</body></html>");}
}
HttpServlet
抽象类的设计
HttpServlet
类中提供了 service(HttpServletRequest, HttpServletResponse)
方法,这个方法是 HttpServlet
自己的方法,不是从 Servlet
继承来的
在 HttpServlet
重写自父类的 service(ServletRequest, ServletResponse)
方法中会把ServletReques
t和 ServletResponse
强转成 HttpServletRequest
和 HttpServletResponse
,然后调用 service(HttpServletRequest, HttpServletResponse)
方法,这就不用自己去强转请求和响应对象了
@Override
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);
}
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;try {ifModifiedSince = req.getDateHeader(HEADER_IFMODSINCE);} catch (IllegalArgumentException iae) {ifModifiedSince = -1;}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);}
}
通过这些细可以发现,HttpServlet
的目的是让我们在进行基于 HTTP
请求的 Web
开发时更加简单,让开发者把更多的精力放在业务逻辑上去,而不是放在网络请求的解析上
Servlet
细节
Servlet
与单例
Servlet
是单例的,在 web
容器当中,在服务器关闭之前,自始至终就是同一个 Servlet
。即同时并发的请求也是由同一个 servlet
处理的。这样的好处是能够减少对象的创建,提升性能,但是我们在开发 Servlet
的时候需要注意线程安全问题,尽量不要定义有状态的全局变量
多个映射路径
一个 Servlet
,可以绑定一个或多个映射路径,通过这些映射路径发送的请求都会到达该Servlet
对于 web.xml
可以如下配置,配置多个 servlet-mapping
,它们的 servlet-name
一致,但是 url-pattern
不一致
<servlet><!--3、 servlet的内部名称--><servlet-name>firstServlt</servlet-name><!--4、 servlet的类全路径名--><servlet-class>com.example.servlet_01.FirstServlet</servlet-class>
</servlet><servlet-mapping><!--2、 servlet的映射内部名称,通过他可以找到servlet的内部名称,不区分大小写--><servlet-name>firstServlt</servlet-name><!--1、 请求servlet的映射路径--><url-pattern>/first-servlet1</url-pattern>
</servlet-mapping><servlet-mapping><!--2、 servlet的映射内部名称,通过他可以找到servlet的内部名称,不区分大小写--><servlet-name>firstServlt</servlet-name><!--1、 请求servlet的映射路径--><url-pattern>/first-servlet2</url-pattern>
</servlet-mapping>
或者在一个 servlet-mapping
中配置多个 url-pattern
<servlet-mapping><!--2、 servlet的映射内部名称,通过他可以找到servlet的内部名称,不区分大小写--><servlet-name>firstServlt</servlet-name><!--1、 请求servlet的映射路径--><url-pattern>/first-servlet3</url-pattern><url-pattern>/first-servlet4</url-pattern><url-pattern>/first-servlet5</url-pattern>
</servlet-mapping>
对于采用 @WebServlet
注解的方式,那就更简单了,该注解的 value
是一个 String
数组,因此我们直接传递多个路径地址的数组就行了
@WebServlet({"/generic-servlet","/generic-servlet2","/generic-servlet3"})
映射路径通配符
Servlet
映射路径可以使⽤通配符 *
,通配符可以匹配任何的字符串
- 如果路径是
/*
结尾,则表示匹配具有指定前缀路径的所有的路径(优先级最高)。它会拦截所有的请求,包括访问直接静态资源、jsp
文件的请求 - 如果路径是
*. 扩展名
,则表示匹配后缀结尾的任意路径 - 这两种格式不能混用
Servlet
映射路径优先级
- 对于某个具体的访问路径,如果存在该路径的
Servlet
映射,那么该Servlet
优先级最高 - 如果不存在,那么具有
/*
结尾的通配符路径的Servlet
优先级最高 - 如果不存在,那么具有
*.后缀名
结尾的通配符路径的Servlet
优先级最高 - 如果不存在,那么最后是具有
/
默认路径通配符的Servlet
,它也被称为默认Servlet
如果使用了 /*
通配符 Servlet
,那么当该 Servlet
中存在请求 include、forward、redirect
到其他没有显示指定路径的资源时(比如转发到某个 jsp
、静态资源等),那么这个请求将会一直在这个 /*
通配符 Servlet
中循环直到抛出异常,因为转发的路径并没有在第一步匹配到具体的 Servlet
的映射,进而又匹配到了这个通配符 Servlet
本身,而这个通配符 Servlet
中又再一次进行了转发
JspServlet
对于以 *.jsp
结尾的访问路径,默认是被一个在 tomcat
容器的 conf/web.xml
配置文件中配置的 JspServlet
处理的,名为 jsp
<servlet><servlet-name>jsp</servlet-name><servlet-class>org.apache.jasper.servlet.JspServlet</servlet-class><init-param><param-name>fork</param-name><param-value>false</param-value></init-param><init-param><param-name>xpoweredBy</param-name><param-value>false</param-value></init-param><load-on-startup>3</load-on-startup>
</servlet>
它的映射如下
<servlet-mapping><servlet-name>jsp</servlet-name><url-pattern>*.jsp</url-pattern><url-pattern>*.jspx</url-pattern>
</servlet-mapping>
默认的 Servlet
Java Web
应用的一切请求都是在请求对应的 Servlet
,动态的资源就不必说了,有自己的路径,对于静态资源,比如 html、css、js
、图片等,虽然在请求 URL
中看起来是访问的静态资源的地址,但是实际上仍然是通过一个默认 Servlet
查找资源的,没找到就返回 404
默认 servlet
也是在 tomcat
容器在 conf/web.xml
配置文件中帮我们配置的,名为 default
<servlet><servlet-name>default</servlet-name><servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class><init-param><param-name>debug</param-name><param-value>0</param-value></init-param><init-param><param-name>listings</param-name><param-value>false</param-value></init-param><load-on-startup>1</load-on-startup>
</servlet>
它的映射如下
<servlet-mapping><servlet-name>default</servlet-name><url-pattern>/</url-pattern>
</servlet-mapping>
所以说,静态资源的请求,会被这个由 tomcat
为我们配置的 Default
拦截并处理,在该 Servlet
中,会尝试查找请求 URL
路径对应的静态资源,找到了就返回该资源,找不到就返回 404
如果在项目中自定义了映射路径为 /
的 Servlet
,那么会覆盖容器为我们配置的Default
,转而走我们自己配置的 Servlet
的逻辑
Servlet
初始化时机
Servlet
在默认情况下,只有当对应的请求第一次到来时才会初始化,也可以配置在启动服务器时立即初始化某些 Servlet
。这样的好处是,在第一次请求的时候不会再创建 Servlet
对象了,提升处理速度,缺点就是启动服务器时会更多的对象,可能会延长服务器启动时间
对于 web.xml
可以如下配置,在 servlet
标签中添加一个 load-on-startup
子标签,该标签用于指示容器是否应该在 web
应用程序启动的时候就加载这个 Servlet
(实例化并调用其 init()
方法)
<servlet><servlet-name>firstServlt</servlet-name><servlet-class>com.example.servlet_01.FirstServlet</servlet-class><load-on-startup>1</load-on-startup>
</servlet>
标签中填写的是启动的优先级
- 它的值必须是一个整数,当值为
0
或者大于0
时,表示容器在应用启动时就加载并初始化这个servlet
。值越小,该servlet
的优先级越高,应用启动时就越先加载 - 如果该元素的值为负数或者没有设置,则容器会当
Servlet
被请求时再加载 - 当值相同时,容器就会自己选择顺序来加载
- 对于
@WebServlet
注解,可以在loadOnStartup
属性中配置该特性
Web开发之Servlet相关推荐
- JavaEE Web开发之Servlet篇
一.Servlet基础 a) Servlet和JSP的关系 1. Servlet是一个运行在服务器端的Java程序,它可以动态地生成web页面,是属于客户和服务器响应的中间层.可以说JSP就是一个 ...
- Java服务器接口快速开发之Servlet详细教程
今日科技快讯 7月14日晚间消息,据乐视网刚刚披露的业绩预告,公司预计2017年上半年亏损6.37亿至6.42亿元,上年同期盈利2.84亿元.报告期内,乐视网资产减值损失计提规模较大约为2.3亿元,其 ...
- 移动web开发之rem布局(rem基础、媒体查询、 less 基础、rem适配方案)
移动web开发之rem布局 一.rem基础 rem单位 rem (root em)是一个相对单位,类似于em,em是父元素字体大小. 不同的是rem的基准是相对于html元素的字体大小. 比如,根元素 ...
- 17. 【移动Web开发之flex布局】
文章目录 [移动Web开发之flex布局]前端小抄(17) 一.flex布局体验 1.1 传统布局与flex布局 1.2 初体验 二.flex布局原理 2.1 布局原理 三.flex布局父项常见属性 ...
- 「学习笔记」移动Web开发之flex布局9
「学习笔记」移动Web开发之flex布局9 一.flex布局体验 1.1 传统布局与flex布局 1.2 初体验 二.flex布局原理 2.1 布局原理 三.flex布局父项常见属性 3.1 常见父项 ...
- WEB开发之HTML与CSS够用即可-庞永旺-专题视频课程
WEB开发之HTML与CSS够用即可-113人已学习 课程介绍 讲解常用的HTML标签与CSS样式.这些常用的HTML标签与CSS样式都是本人多年从业经验的总结.只要熟练我总结的HTM ...
- 移动WEB开发之-REM(rem)布局
目录 移动WEB开发之REM布局 rem基础 rem单位 媒体查询 什么是媒体查询 媒体查询语法规范 1.mediatype 查询类型 2.关键字 3.媒体特性 4.媒体查询书写规则 less 基础 ...
- Python Web开发之WSGI
Python Web开发之WSGI WSGI(全称Web Server Gate Interface,Web服务器网关接口)是Python为了规范和简化Web服务开发过程,定义了一种Web服务器和应用 ...
- Swift Web 开发之 Vapor - 入门(一)
简介 Vapor 是一个基于纯 Swift 构建出的 Web 开发框架,目前可以运行在 macOS 和 Ubuntu ,用于构建出漂亮易用的网站或者 API 服务. 官方称是用的最多的 Swift w ...
- Swift Web 开发之 Vapor - 模版 Leaf(三)
模版引擎,对现在的 Web 开发极为重要,几乎所有主流 Web 框架都会支持一种或多种模版引擎,模版引擎可以分离用户界面和业务逻辑,工作原理主要是一种翻译,后端对特定的标记.语法.变量等渲染后再输送给 ...
最新文章
- Storm入门之第一章
- 深度学习(06)-- Network in Network(NIN)
- 来了!苹果二代AirPods 3月发布 全黑配色加入
- 电商页面设计排版没有思路?可临摹PSD分层模板,诠释基础版式大招帮你轻松搞定!
- python提高运行效率_提高CPU密集型任务执行效率——Python多进程介绍,内附实例代码...
- 每天坚持跑步到底会不会瘦呢?
- mysql误删除ibdata1以及日志ib_logfile*
- python处理期货数据_用Python下载并分析期货持仓数据
- 计算机操作系统第四版第一章知识点归纳总结
- mysql 显示 乱码_MySQL 中文显示乱码
- 企业级负载均衡LVS集群——DR模式下的(加权)轮询调度器、DR模式下的健康检测(ldirectord)
- mysql实习报告总结_MySQL数据库实训报告 实训心得
- 【PyTorch 自然语言处理】传统 NLP 快速回顾(计算语言学)
- 联想电脑虚拟化开启方法
- 郑州财经学院第54次全国计算机,郑州财经学院第二期教师博士班开班
- java3D实现空间立方体,纯CSS3实现一个旋转的3D立方体盒子
- Java项目:SSH自驾游管理系统
- intellij创建快捷方式到桌面
- Java实战项目之人力资源管理系统【源码+课后指导】_Java毕业设计/计算机毕业设计
- 30天免费试用 ▎(IDM) 极速下载工具