Servlet
是基于 Jakarta
技术的 Web
组件,由容器管理,可生成动态内容。与其他基于 Jakarta
技术的组件一样,servlet
是独立于平台的 Java
类,它们被编译为与平台无关的字节码,这些字节码可以动态加载到支持 Jakarta
技术的 Web
服务器中并由其运行。容器,有时也称为 servlet
引擎,是提供 servlet
功能的 Web
服务器扩展。Servlet
通过 servlet
容器实现的请求/响应范式与 Web
客户端交互。
Servlet
容器是 Web
服务器或应用程序服务器的一部分,它提供发送请求和响应的网络服务、解码基于 MIME
的请求以及格式化基于 MIME
的响应。Servlet
容器还通过其生命周期包含和管理 Servlet
。
Servlet
容器可以内置到主机 Web
服务器中,也可以通过该服务器的本机扩展 API
作为附加组件安装到 Web
服务器。Servlet
容器也可以内置于或可能安装在支持 Web
的应用程序服务器中。
所有 Servlet
容器都必须支持 HTTP
作为请求和响应的协议,但可以支持其他基于请求/响应的协议,例如 HTTPS
(基于 SSL
的 HTTP
)。容器必须实现的 HTTP
规范的必需版本是 HTTP/1.1
和 HTTP/2
。
Java SE 8
是必须用来构建 Servlet
容器的底层 Java
平台的最低版本。
以下是一个典型的事件序列:
Web
浏览器)访问 Web
服务器并发出 HTTP
请求。Web
服务器接收并传递给 Servlet
容器。Servlet
容器可以在与主机 Web
服务器相同的进程中运行,也可以在同一主机上的不同进程中运行,或者在与其处理请求的 Web
服务器不同的主机上运行。Servlet
容器根据其Servlet
的配置确定调用哪个 Servlet
,并使用代表请求和响应的对象调用它。Servlet
使用请求对象来找出远程用户是谁、POST
作为此请求的一部分发送的HTTP
参数以及其他相关数据。Servlet
执行它编程的任何逻辑,并生成数据发送回客户端。它通过响应对象将此数据发送回客户端。Servlet
处理完请求后,Servlet
容器会确保正确刷新响应,并将控制权返回给主机 Web
服务器。在默认情况下,当Web
客户第一次请求访问某个Servlet
时,Web
容器会创建这个Servlet
的实例。当设置了web.xml
中的子元素后,Servlet
容器在启动Web
应用时,将按照指定顺序创建并初始化这个Servlet
。设置的数值大于0
即可。例如:
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.ydlclass.servlet.HelloServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
先看与Servlet
生命周期有关的三个方法:init(),
service()
, destroy()
. Servlet
生命周期可被定义为从创建直到毁灭的整个过程。以下是三个方法分别对应的Servlet
过程:
void init(ServletConfig argo)
:执行时机,当Servlet
实例被创建时候调用,做初始化工作。ServletConfig
的配置对象,初始化的时候可以进行配置。void service(ServletRequest arg0,ServletResponse arg1)
:执行时机,当一个请求来请求当前的Servlet
的时候被调用,处理当前的Servlet
的业务逻辑并把响应返回给浏览器。void destroy()
:执行时机,是Servlet
的实例对象被销毁的时候调用,做一些收尾或清理工作。在调用
destroy()
方法后,Servlet
由JVM
的垃圾回首器进行垃圾回收。
还有一些其他方法:
String getServletinfo()
:获得当前的一些属性信息。ServletConfig getServletConfig()
:获得ServletConfig
的配置对象现在我们来详细讨论Servlet
生命周期的方法:
Servlet
被装载后,Servlet
容器创建一个Servlet
实例并且调用Servlet
的init()
方法进行初始化在Servlet
生命周期中init()
方法只被调用一次。
当用户调用一个Servlet
时,Servlet
容器就会创建一个Servlet
实例,每一个用户请求都会产生一个新的线程,init()
方法简单的创建或加载一些数据,这些数据将会被用在Servlet
的整个生命周期。
init()
方法的定义如下:
public void init() throws ServletException {
// 初始化代码...
}
service()
方法是执行实际任务的主要方法。Servlet
容器(即 Web
服务器)调用 service()
方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端。
每次服务器接收到一个 Servlet
请求时,服务器会产生一个新的线程并调用服务。service()
方法检查 HTTP
请求类型(GET
、POST
、PUT
、DELETE
等),并在适当的时候调用doGet()
、doPost()
等方法。
service()
的定义如下:
public void service(ServletRequest request, ServletResponse response)
throws ServletException, IOException{
// service()代码...
}
destroy()
方法只会被调用一次,在Servlet
生命周期结束时被调用。destroy()
方法可以让Servlet
关闭数据库连接、停止后台、把cookie
列表或点击计数器写入到磁盘,并执行其他类似的清理活动。 在调用destroy()
方法之后,Servlet
对象被标记为垃圾回收。
destroy()
方法的定义如下所示:
public void destroy() {
// 终止化代码...
}
Servlet
时,init()
方法会被执行,而且也会执行service()
方法。service()
方法,不再执行init()
方法。Web
容器时会调用destroy()
方法。当服务器接收到一个请求,就要有一个Servlet
去处理这个请求,所以完成一个Servlet
通常需要两步走。
java
程序定义一个Servlet
。Servlet
确定这个Servlet
要处理哪一个请求。javax.servlet.Servlet
接口。javax.servlet.GenericServlet
类。javax.servlet.http.HttpServlet
类。我们在日常开发中一般会使用第三种方法来进行Servlet
的创建,前两种方法理解即可。
注意
:创建Servlet
文件后,需要在web.xml
文件中完成Servlet
配置,才可以使用。
通过实现Servlet
接口,这个接口定义了servlet
的生命周期,所有的方法需要我们实现。
public class UserServlet 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 {
servletResponse.getWriter().print("hello servlet
");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
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 getServletInfo() {
return "";
}
public void init() throws ServletException {
}
public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
public String getServletName() {
return this.config.getServletName();
}
....
}
public class UserServlet extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
servletResponse.getWriter().print("hello servlet
");
}
}
Http
只是会根据请求的类型进行特殊的调用
package javax.servlet.http;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.text.MessageFormat;
import java.util.Enumeration;
import java.util.ResourceBundle;
import javax.servlet.DispatcherType;
import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
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(405, msg);
} else {
resp.sendError(400, msg);
}
}
protected long getLastModified(HttpServletRequest req) {
return -1L;
}
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(405, msg);
} else {
resp.sendError(400, msg);
}
}
// 还是会调用它,只是会根据请求的类型进行特殊的调用
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String method = req.getMethod();
long lastModified;
if (method.equals("GET")) {
lastModified = this.getLastModified(req);
if (lastModified == -1L) {
this.doGet(req, resp);
} else {
long ifModifiedSince;
try {
ifModifiedSince = req.getDateHeader("If-Modified-Since");
} catch (IllegalArgumentException var9) {
ifModifiedSince = -1L;
}
if (ifModifiedSince < lastModified / 1000L * 1000L) {
this.maybeSetLastModified(resp, lastModified);
this.doGet(req, resp);
} else {
resp.setStatus(304);
}
}
} else if (method.equals("HEAD")) {
lastModified = this.getLastModified(req);
this.maybeSetLastModified(resp, lastModified);
this.doHead(req, resp);
} else if (method.equals("POST")) {
this.doPost(req, resp);
} else if (method.equals("PUT")) {
this.doPut(req, resp);
} else if (method.equals("DELETE")) {
this.doDelete(req, resp);
} else if (method.equals("OPTIONS")) {
this.doOptions(req, resp);
} else if (method.equals("TRACE")) {
this.doTrace(req, resp);
} else {
String errMsg = lStrings.getString("http.method_not_implemented");
Object[] errArgs = new Object[]{method};
errMsg = MessageFormat.format(errMsg, errArgs);
resp.sendError(501, errMsg);
}
}
...
}
HttpServletRequest
和ServletRequest
都是接口HttpServletRequest
继承自ServletRequest
HttpServletRequest
比ServletRequest
多了一些针对于Http协议的方法。 例如:getHeader()
, getMethod()
, getSession()
public class HttpServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet方法被调用");
resp.getOutputStream().write("hello Loanon".getBytes());
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost方法被调用");
doGet(req, resp);
}
}
配置web.xml
<servlet>
<servlet-name>helloServletservlet-name>//随便写,但是要和下面的对应一样
<servlet-class>com.r1.servlet.ServletDemoservlet-class>//具体的类路径
servlet>
<servlet-mapping>
<servlet-name>helloServletservlet-name>//和上面的名字一样
<url-pattern>/hellourl-pattern>//浏览器输入的域名
servlet-mapping>
中配置的项必须与url
完全精确匹配。
<servlet-mapping>
<servlet-name>MyServletservlet-name>
<url-pattern>/user/users.htmlurl-pattern>
<url-pattern>/index.htmlurl-pattern>
<url-pattern>/user/addUserurl-pattern>
servlet-mapping>
当在浏览器中输入如下几种url
时,都会被匹配到该servlet
http://localhost:8080/appDemo/user/users.html
http://localhost:8080/appDemo/index.html
http://localhost:8080/appDemo/user/addUser
注意:
http://localhost:8080/appDemo/user/addUser?username=Tom&age=23
也会被匹配到MyServlet
。
以“/
”字符开头,并以“/*
”结尾的字符串用于路径匹配
<servlet-mapping>
<servlet-name>MyServletservlet-name>
<url-pattern>/user/*url-pattern>
servlet-mapping>
路径以/user/
开始,后面的路径可以任意。比如下面的url
都会被匹配。
http://localhost:8080/appDemo/user/users.html
http://localhost:8080/appDemo/user/addUser
http://localhost:8080/appDemo/user/bb//sdf/sdf/sdf/updateUser
以“*.
”开头的字符串被用于扩展名匹配
<servlet-mapping>
<servlet-name>MyServletservlet-name>
<url-pattern>*.jspurl-pattern>
<url-pattern>*.dourl-pattern>
servlet-mapping>
则任何扩展名为jsp
或action
的url
请求都会匹配,比如下面的url
都会被匹配
http://localhost:8080/appDemo/user/users.jsp
http://localhost:8080/appDemo/toHome.action
<servlet-mapping>
<servlet-name>MyServletservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
注意:使用扩展名匹配,前面就不能有任何的路径。
4. 缺省匹配,以上都找不到Servlet
,就用默认的Servlet
,配置为
路径匹配和扩展名匹配无法同时设置
匹配方法只有三种 :
/
”字符开头,并以“/*
”结尾)。*.
”开头)三种匹配方法不能进行组合,不要想当然使用通配符。
/user/*.action
是非法的/aa/*/bb
是精确匹配,合法,这里的*
不是通配的含义"
/*
“和”/
"含义并不相同
/*
”属于路径匹配,并且可以匹配所有request
,由于路径匹配的优先级仅次于精确匹配,所以“/*
”会覆盖所有的扩展名匹配,很多404
错误均由此引起,所以这是一种特别恶劣的匹配模式。/
”是servlet
中特殊的匹配模式,切该模式有且仅有一个实例,优先级最低,不会覆盖其他任何url-pattern
,只是会替换servlet
容器的内建default servlet
,该模式同样会匹配所有request
。Tomcat
在%CATALINA_HOME%\conf\web.xm
l文件中配置了默认的Servlet
,配置代码如下:
<servlet>
<servlet-name>defaultservlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServletservlet-class>
<init-param>
<param-name>debugparam-name>
<param-value>0param-value>
init-param>
<init-param>
<param-name>listingsparam-name>
<param-value>falseparam-value>
init-param>
<load-on-startup>1load-on-startup>
servlet>
<servlet>
<servlet-name>jspservlet-name>
<servlet-class>org.apache.jasper.servlet.JspServletservlet-class>
<init-param>
<param-name>forkparam-name>
<param-value>falseparam-value>
init-param>
<init-param>
<param-name>xpoweredByparam-name>
<param-value>falseparam-value>
init-param>
<load-on-startup>3load-on-startup>
servlet>
<servlet-mapping>
<servlet-name>defaultservlet-name>
<url-pattern>/url-pattern>
servlet-mapping>
<servlet-mapping>
<servlet-name>jspservlet-name>
<url-pattern>*.jspurl-pattern>
<url-pattern>*.jspxurl-pattern>
servlet-mapping>
“
/*
”和“/
”均会拦截静态资源的加载,需要特别注意