一、Servlet 的定义
Servlet 是 JavaWeb 的三大组件之一,它属于动态资源。Servlet 的作用是处理请求,服务器会把接收到的请求交给 Servlet 来处理,在 Servlet 中需要:
- 接收请求数据;
- 处理请求;
- 完成响应;
二、Servlet 的生命周期
我们先创建一个实例来实现 Servlet 接口:
public class Servlet01 implements Servlet {
@Override
public void init(ServletConfig servletConfig) throws ServletException {
}
@Override
public ServletConfig getServletConfig() {
return null;
}
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
可以看到该实例默认实现了5个方法,我们主要讲解其中的3个。
方法:
- void init(ServletConfig):出生之后(1次);
- void
service
(ServletRequest request, ServletResponse response):每次处理请求时都会被调用; - void destroy():临死之前(1次);
特征:
- 单例的,一个类只有一个对象;
- 多线程的,不是线程安全的。
1、Servlet 出生
-
默认情况下,服务器会在第一次访问 Servlet 时创建它,如果是在服务器启动时就创建 Servlet ,那么需要在
web.xml
中配置。 -
对于一个 Servlet ,服务器只会
创建一次
,当访问 Servlet 时, 服务器会首先检查该 Servlet 是否创建过,如果创建过直接拿过来用,如果没有才会通过反射来创建该 Servlet 实例。 -
Servlet 出生后会立即调用
init()
方法,而且这个方法之后被调用一次,所以一些对 Servlet 的初始化操作可以放在该方法中。
2、Servlet 服务
当服务器每次接收到请求时,都会调用 service
方法,该方法是会被多次调用的。
3、Servlet 销毁
- Servlet 是不会轻易消失的,通常在服务器关闭的时候才会销毁,在服务器关闭时,他会调用
destroy
方法,然后销毁 Servlet,我们可以将一些资源的释放放到该方法中。
三、Servlet 中的接口
在 Servlet 中有三个接口类型的参数:
- ServletConfig:
init()
方法的参数,它表示Servlet配置对象,它对应Servlet的配置 - ServletRequest:
service()
方法的参数,它表示请求对象,它封装了所有与请求相关的数据,里面全是请求头之类get
方法,它是由服务器创建的; - ServletResponse:
service()
方法的参数,它表示响应对象,里面很多响应的set
方法,在 service() 方法中完成对客户端的响应需要使用这个对象;
1、ServletRequest 和 ServletResponse
ServletRequest 和 ServletResponse 是 Servlet 的
service()
方法的两个参数:
一个是
请求对象
,可以从 ServletRequest 对象中获取请求数据;
一个是响应对象
,可以使用 ServletResponse 对象完成响应。
- ServletRequest 和 ServletResponse 的实例由服务器创建,然后传递给
service
方法,如果要使用 HTTP 相关内容,那么需要将 ServletRequest 强转成 HttpServletRequest ,这很烦,因为我们经常要这么做,后面有一个类可以解决这个问题。
2、ServletRequest
- String getHeader(String var1):获取指定请求头的值;
- String getMethod():获取请求方法,例如GET或POST;
- String getParameter(String paramName):获取指定请求参数的值;
- void setCharacterEncoding(String encoding):设置请求体的编码!
request.setCharacterEncoding(“utf-8”)之后,再通过
getParameter()
方法获取参数值时,那么参数值都已经通过了转码,即转换成了UTF-8编码。所以,这个方法必须在调用 getParameter() 方法之前调用!
3、ServletResponse
-
PrintWriter getWriter():获取字符响应流,使用该流可以向客户端输出响应信息。
-
ServletOutputStream getOutputStream():获取字节响应流,当需要向客户端响应字节数据时,需要使用这个流;
-
void setCharacterEncoding(String encoding):用来设置字符响应流的编码;
-
void setHeader(String name, String value):向客户端添加响应头信息。
例如
setHeader(“Refresh”, “5;url=http://www.baidu.cn”)
,表示 5 秒后自动刷新到http://www.baidu.cn; -
void
setContentType
(String contentType):该方法是 setHeader(“content-type”, “xxx”) 的简便方法,即用来添加名为content-type
响应头的方法。content-type响应头用来设置响应数据的MIME
类型。-例如要向客户端响应 jpg 的图片,那么可以 setContentType(“
image/jepg
”) ,如果响应数据为文本类型,那么还要同时设置编码
-例如setContentType(“text/html;chartset=utf-8
”)表示响应数据类型为文本类型中的html类型,并且该方***调用 setCharacterEncoding(“utf-8”) 方法;
4、ServletConfig
Servlet 的配置信息,即
web.xml
文件中的<servlet>
元素。
- 一个 ServletConfig 对象对应着一个 servlet 元素的配置信息(servlet-name,servlet-class)
- getServletName():获取的是
<servlet-name>
- getServletContext():获取的是 Servlet 上下文对象
四、Servlet 的实现
1、实现Servlet有三种方式:
- 实现 javax.servlet.Servlet 接口;
- 继承 javax.servlet.GenericServlet 类;
- 继承 javax.servlet.http.HttpServlet 类;
通常我们会去继承 HttpServlet 类来完成我们的 Servlet
2、GenericServlet
GenericServlet是Servlet接口的实现类,我们可以通过继承GenericServlet来编写自己的Servlet。
GenericServlet 还实现了ServletConfig 接口,所以可以直接调用getInitParameter()、getServletContext() 等 ServletConfig 的方法。
源代码:
public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
private static final long serialVersionUID = 1L;
private transient ServletConfig config;
public GenericServlet() {
}
public void destroy() {
}
public String getInitParameter(String name) {
return this.getServletConfig().getInitParameter(name);
}
public Enumeration<String> getInitParameterNames() {
return this.getServletConfig().getInitParameterNames();
}
public ServletConfig getServletConfig() {
return this.config;
}
public ServletContext getServletContext() {
return this.getServletConfig().getServletContext();
}
public String getServletInfo() {
return "";
}
public void init(ServletConfig config) throws ServletException {
this.config = config;
this.init();
}
public void init() throws ServletException {
}
public void log(String msg) {
this.getServletContext().log(this.getServletName() + ": " + msg);
}
public void log(String message, Throwable t) {
this.getServletContext().log(this.getServletName() + ": " + message, t);
}
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
public String getServletName() {
return this.config.getServletName();
}
}
3、HttpServlet
HttpServlet 类是 GenericServlet 的子类,它提供了对 HTTP 请求的特殊支持,所以通常我们都会通过继承 HttpServlet 来完成自定义的 Servlet 。
-
HttpServlet 覆盖了 service() 方法
HttpServlet类中提供了
service(HttpServletRequest,HttpServletResponse)
方法,这个方法是 HttpServlet 自己的方法,不是从 Servlet 继承来的。在 HttpServlet的service(ServletRequest,ServletResponse) 方法中会把ServletRequest和ServletResponse 强转成 HttpServletRequest 和 HttpServletResponse ,然后调用,所以这样一来就不用我们自己强转了。 -
doGet() 和 doPost()
在 HttpServlet 的 service(HttpServletRequest,HttpServletResponse) 方***去判断当前请求是 GET 还是 POST ,如果是 GET 请求,那么会去调用本类的doGet() 方法,如果是 POST 请求会去调用 doPost() 方法,这说明我们在子类中去覆盖 doGet() 或 doPost()
方法即可。
4、启动创建 Servlet
1、< load-on-startup >
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>cn.lsu.servlet.HelloServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
在<servlet>
元素中配置<load-on-startup>
元素可以让服务器在启动时就创建该Servlet,其中<load-on-startup>
元素的值必须是大于等于的整数,它的使用是服务器启动时创建 Servlet 的顺序。
2、< url-pattern >
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<url-pattern>
是<servlet-mapping>
的子元素,用来指定Servlet的访问路径,即URL。
可以在<servlet-mapping>
中给出多个<url-pattern>
,这样它所绑定的所有
的 URL 都是指向这个 Servlet 的。
还可以使用通配符:
<url-pattern>.do</url-pattern>:/abc/def/ghi.do、/a.do,都匹配
<url-pattern>/*<url-pattern>:匹配所有URL;
5、web.xml文件的继承
在 Tomcat 的conf/web.xml
路径下有一个所有的 web.xml
共同的父文件。
<?xml version="1.0" encoding="ISO-8859-1"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<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>
<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>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.jsp</url-pattern>
<url-pattern>*.jspx</url-pattern>
</servlet-mapping>
<session-config>
<session-timeout>30</session-timeout>
</session-config>
<!-- 这里省略了大概4000多行的MIME类型的定义,这里只给出两种MIME类型的定义 -->
<mime-mapping>
<extension>bmp</extension>
<mime-type>image/bmp</mime-type>
</mime-mapping>
<mime-mapping>
<extension>htm</extension>
<mime-type>text/html</mime-type>
</mime-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
6、ServletContext
6.1、ServletContext概述
服务器会为每个应用创建一个 ServletContext 对象:
- ServletContext对象的创建是在服务器启动时完成的;
- ServletContext对象的销毁是在服务器关闭时完成的。
ServletContext 对象的作用 是在整个Web应用
的动态资源之间共享数据
!
6.2、获取ServletContext
ServletConfig
下的 getServletContext();GenericServlet
下的 getServletContext();(包括子类 HttpServlet )- HttpSession 下的 getServletContext();
- ServletContextEvent 下的 getServletContext();
在 Servlet 中获取 ServletContext 对象:
-
在void init(ServletConfig config)中:
ServletContext context = config.getServletContext();
ServletConfig类的getServletContext()方法可以用来获取ServletContext对象; -
GenericServlet类有getServletContext()方法,所以可以直接使用this.getServletContext()来获取;
public class MyServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response) {
ServletContext context = this.getServletContext();
}
}
6.3、域对象的功能
ServletContext是JavaWeb四大域对象之一:
- PageContext;
- ServletRequest;
- HttpSession;
- ServletContext;
所有域对象都有存取数据的功能,因为域对象内部有一个`Map`,用来存储数据
6.4、操作数据的方法
- void
setAttribute
(String name, Object value):用来存储一个对象; - Object
getAttribute
(String name):用来获取ServletContext中的数据; - void removeAttribute(String name):用来移除ServletContext中的域属性;
- Enumeration getAttributeNames():获取所有域属性的名称;
6.5、初始化参数
1、应用初始化参数
该参数是每个 Servlet 独有的,是局部参数。
2、公共初始化参数
该参数为所有 Servlet 都可用。
可以在 web.xml
中配置:
<web-app ...>
...
<context-param>(为ServletContext设置的公共初始化参数)
<param-name>paramName1</param-name>
<param-value>paramValue1</param-value>
</context-param>
<context-param>
<param-name>paramName2</param-name>
<param-value>paramValue2</param-value>
</context-param>
</web-app>
使用域对象获取:
ServletContext context = this.getServletContext();
String value1 = context.getInitParameter("paramName1");
String value2 = context.getInitParameter("paramName2");
五、请求和响应
服务器每次收到请求时,都会为这个请求开辟一个新的线程。
1、Request
1.1、概述
request 是 Servlet.service() 方法的一个参数,类型为javax.servlet.http.HttpServletRequest
。在客户端发出每个请求时,服务器都会创建一个request对象,并把请求数据封装到request中,然后在调用 Servlet.service() 方法时传递给 service() 方法,这说明在 service() 方法中可以通过 request 对象来获取请求数据。
request的功能可以分为以下几种:
- 封装了请求头数据;
- 封装了请求正文数据,如果是GET请求,那么就没有正文;
- request是一个域对象,可以把它当成Map来添加获取数据;
- request提供了请求转发和请求包含功能。
一个请求会创建一个request对象,如果在一个请求中经历了多个 Servlet ,那么多个 Servlet 就可以使用 request 来共享数据
。
request 的域方法同 ServletContext 的域方法。
- void
setAttribute
(String name, Object value):用来存储一个对象; - Object
getAttribute
(String name):用来获取ServletContext中的数据; - void removeAttribute(String name):用来移除ServletContext中的域属性;
- Enumeration getAttributeNames():获取所有域属性的名称;
1.2、常用方法
1、获取请求头数据:
- String getHeader(String name):获取指定名称的请求头;
2、获取请求相关的其它方法:
- String getContentType():获取请求类型,如果请求是GET,那么这个方法返回null;如果是POST请求,那么默认为application/x-www-form-urlencoded,表示请求体内容使用了URL编码;
- String getMethod():返回请求方法,例如:GET/POST
- String getCharacterEncoding():获取请求编码;默认ISO-8859-1编码;
- void setCharacterEncoding(String code):设置请求编码,只对请求体有效;
- String
getContextPath()
:返回上下文路径(/项目名),例如:/hello - String getQueryString():返回请求URL中的参数,例如:name=zhangSan
- String
getRequestURI()
:返回请求URI路径,例如:/hello/oneServlet - StringBuffer getRequestURL():返回请求URL路径,例如:
http://localhost/hello/oneServlet
,即返回除了参数以外的路径信息; - String getServletPath():返回Servlet路径,例如:/oneServlet
- String
getRemoteAddr()
:返回当前客户端的IP地址; - String getRemoteHost():返回当前客户端的主机名,但这个方法的实现还是获取IP地址;
- String getScheme():返回请求协议,例如:http;
- String getServerName():返回主机名,例如:localhost
- int getServerPort():返回服务器端口号,例如:8080
1.3、实际应用
防盗链的应用:
可以使用request.getAttribute(“Referer”)
如果不是当前页面,那么属于盗链,则跳转到当前页面如果是从地址栏直接输入URL,那么Referee返回的是null。
2、Response
2.1、概述
response 是 Servlet # service 方法的一个参数,类型为javax.servlet.http.HttpServletResponse
。在客户端发出每个请求时,服务器都会创建一个 response 对象,并传入给 Servlet.service() 方法。response 对象是用来对客户端进行响应的,这说明在 service() 方法中使用 response 对象可以完成对客户端的响应工作。
response
对象的功能分为以下四种:
- 设置响应头信息;
- 发送状态码;
- 设置响应正文;
- 重定向。
2.2、响应正文
response是响应对象,向客户端输出响应正文(响应体) 可以使用response的响应流:
- PrintWriter out = response.getWriter():获取字符流;
- ServletOutputStream out = response.getOutputStream():获取字节流;
如果响应正文内容为字符(html),那么使用response.getWriter(),如果响应内容是字节(图片等),例如下载时,那么可以使用response.getOutputStream()
在一个请求中,不能同时使用这两个流!
-
在使用 response.getWriter() 时需要注意默认字符编码为 ISO-8859-1,如果希望设置字符流的字符编码为utf-8,可以使用response.
setCharaceterEncoding
(“utf-8”) 来设置。这样可以保证输出给客户端的字符都是使用UTF-8编码的! -
但客户端浏览器并不知道响应数据是什么编码的!如果希望通知客户端使用UTF-8来解读响应数据,那么还是使用response.
setContentType
(“text/html;charset=utf-8”)方法比较好,因为这个方法不只会调用response.setCharaceterEncoding(“utf-8”),还会设置content-type响应头,客户端浏览器会使用 content-type 头来解读响应数据。
缓冲区:
- response.getWriter() 是 PrintWriter 类型,所以它有缓冲区,缓冲区的默认大小为
8KB
。也就是说,在响应数据没有输出8KB之前,数据都是存放在缓冲区
中,而不会立刻发送到客户端。当 Servlet 执行结束后,服务器才会去刷新流,使缓冲区中的数据发送到客户端。 - 可以使用 response.flushBuffer() 方法手动刷新缓冲区。
字节响应流:
将一张图片转为字节流写入到response中
//读取图片,使用commons-io包的方法。
byte[] image = IOUtils.toByteArray(new FileInputStream(this.getServletContext().getRealPath("/images/cat.jpeg")));
response.getOutputStream().write(image);
2.3、设置响应头信息
可以使用 response 对象的setHeader()
方法来设置响应头!使用该方法设置的响应头最终会发送给客户端浏览器!
response.setHeader("Refresh","5; URL=http://www.baidu.cn"):5秒后自动跳转到百度主页。
2.4、设置状态码及其他方法
- response.setContentType(“text/html;charset=utf-8”)
- response.setCharacterEncoding(“utf-8”)
- response.setStatus(200)
- response.sendError(404, “您要查找的资源不存在”)
2.5、重定向
当你访问
http://www.sun.com
时,你会发现浏览器地址栏中的URL会变成http://www.oracle.com/us/sun/index.html
,这就是重定向了。
重定向是服务器通知浏览器去访问另一个地址,即再发出另一个请求。
1、设置响应码
- 响应码为302表示重定向。所以完成重定向的第一步就是设置响应码为302。
2、设置Location头
- 因为重定向是通知浏览器发出第二个请求,所以浏览器需要知道第二个请求的URL,所以完成重定向的第二步是设置
Location
头,指定第二个请求的URL地址。
public class AServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setStatus(302);
response.setHeader("Location", "http://www.baidu.cn");
}
}
当访问 AServlet 后,会通知浏览器重定向到百度主页。客户端浏览器解析到响应码为302后,就知道服务器让它重定向,所以它会马上
获取响应头Location,然发出第二个请求。
不过有更方便的方法:
response.sendRedirect()方***设置响应头为302,以设置Location响应头。
如果要重定向的URL是在同一个服务器内,那么可以使用相对路径,例如:
public class AServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.sendRedirect("/hello/BServlet");
//注意是-/项目名/路径(请求URI)
}
}
3、GET 和 POST 的区别
GET请求和POST请求的区别:
GET请求:
- 请求参数会在浏览器的地址栏中显示,所以不安全;
- 请求参数长度限制长度在1K之内;
- GET请求没有请求体,无法通过 request.setCharacterEncoding() 来设置参数的编码;
POST请求:
- 请求参数不会显示浏览器的地址栏,相对安全;
- 请求参数长度没有限制;
- 无论是GET|POST请求,都可以使用相同的API来获取请求参数。
- 请求参数有一个key一个value的,也有一个key多个value的。
4、请求转发
无论是请求转发还是请求包含,都表示由多个 Servlet 共同来处理一个请求。例如 Servlet1 来处理请求,然后 Servlet1 又转发给 Servlet2 来继续处理这个请求。
在AServlet中,把请求转发到BServlet:
参数是Servlet路径(servlet-mapping
中的url-pattern
)—相当于/项目名
public class AServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
RequestDispatcher rd = request.getRequestDispatcher("/BServlet");
rd.forward(request, response);
}
5、转发和重定向的区别
转发是由服务端
进行的页面跳转:
重定向是由客户端
进行的页面跳转: