在了解Servlet之前,我们首先需要知道Servlet的生命周期,Servlet的生命周期分为三个阶段:
1. init
2. service
3. destroy
一个Servlet一生只实例化一次(除非实现了SingleThreadModel,该api在2.4已经被标记为@deprecated),只会调用一次init和destroy,所以servlet是单例多线程。
了解Servlet需要知道的知识点:
1. servlet三个生命周期方法在什么时候执行
2. ServletConfig和ServletContext的是什么
3. 什么是servlet-mapping,规则是什么
一、Servlet三个生命周期方法在什么时候执行
- 初始化init方法会在服务器启动时或者第一次请求到来时执行,这取决于web.xml是否设置
<load-on-startup>1</load-on-startup>
,只要设置了loadOnStartup并且值大于0,那么该servlet将会在服务器启动完成之前完成初始化。 - service方法会在客户端请求对应的url-mapping时调用,容器会将请求信息封装成一个HttpServletRequest,将输出信息封装成一个HttpServletResponse,然后作为参数传递到service方法中。
- destroy方法将会在服务器停止时调用。
那么,这三个生命周期方法有什么作用呢?init方法通常做一些初始化的操作,比如数据库连接,init方法中会得到一个ServletConfig,可通过ServletConfig.getInitParameter(name)
拿到初始化的值来进行初始化工作,当然,你也可以直接定义局部变量进行初始化工作(除非你确定该变量不会变动,否则尽量通过配置ServletConfig InitParameter的方式,方便维护)。service方法是用来响应客户端请求输出内容返回到客户端的,一般来说,我们更倾向于重写doGet和doPost请求来响应不同请求方式,一个请求只会执行一个Servlet。destroy主要用于一些资源释放的操作。
二、ServletConfig和ServletContext的是什么
上面我们提到了ServletConfig,那么什么是ServletConfig,怎么配置InitParameter,和ServletContext有什么区别?
ServletConfig是一个作用域仅限于当前Servlet的容器,而ServletContext能被所有Servlet共享。
对于ServletConfig来说,配置InitParameter只能在web.xml中的webapp.servlet.init-param进行配置,它没有对应的setXXX方法来设置参数。简单的配置如下:
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.hxl.MyServlet</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>hxl</param-value>
</init-param>
<init-param>
<param-name>date</param-name>
<param-value>2018</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
我们在Java程序中以下代码可以得到配置的参数值
public void init(ServletConfig config) throws ServletException {
Enumeration<?> names = config.getInitParameterNames();
while(names.hasMoreElements()) {
String key = (String) names.nextElement();
System.out.println(key+":"+config.getInitParameter(key));
}
}
打印如下:
date:2018
name:hxl
对于ServletContext来说,可以通过webapp.context-param配置InitParameter,除了InitParameter,ServletContext还可以通过setAttribute的方式设置属性,任何Servlet都可以通过getAttribute拿到该属性,所以,attribute的使用需要谨慎小心,因为可能会存在并发的问题。记住,ServletContext是所有Servlet共享的。
xml配置InitParameter:
<context-param>
<param-name>sql</param-name>
<param-value>mysql</param-value>
</context-param>
Java代码获取初始化参数,设置属性
ServletContext servletContext = config.getServletContext();
//获取InitParameter
String parameter = servletContext.getInitParameter("sql");
System.out.println(parameter);
//设置属性,记录请求数
servletContext.setAttribute(TOTAL_REQUEST, 0);
ServletContext servletContext = request.getSession().getServletContext();
//忽略并发代码...
int total =Integer.parseInt(servletContext.getAttribute(TOTAL_REQUEST)+"");
System.out.println("totalRequest:"+(total+1));
servletContext.setAttribute(TOTAL_REQUEST, total+1);
这样,当我们每请求一次,totalRequest就会+1
totalRequest:1
totalRequest:2
totalRequest:3
三、什么是servlet-mapping,规则是什么
servlet-mapping是用于将客户端请求映射到指定的servlet,匹配的规则如下:
1. Map exact URL–精确匹配
2. Map wildcard paths–通配符匹配
3. Map extensions–扩展名匹配
4. Map to the default servlet–匹配到默认servlet
例如:
Servlet1 映射到 /abc/*
Servlet2 映射到 /*
Servlet3 映射到 /abc
Servlet4 映射到 *.do
当请求URL为“/abc/a.html”,调用Servlet1。
当请求URL为“/abc”时,调用Servlet3。
当请求URL为“/abc/a.do”时,调用Servlet1。
当请求URL为“/a.do”时,调用Servlet2。
当请求URL为“/xxx/yyy/a.do”时,调用Servlet2。
我们一般都知道匹配规则的前三条,少部分人不知道第四条的default servlet是什么,了解default servlet之前,我们先了解<url-pattern>/</url-pattern>
和<url-pattern>/*</url-pattern>
的区别。
1、如果我们写的servlet(我们起类名为MyServlet)使用的url-pattern是/*,那么会响应一切url请求,就没有default servlet什么事了。
2、如果使用的是/,那么当我们请求的路径是.jsp为后缀的时候(这种情况下也仅仅能处理.jsp结尾的url),该请求不再经过MyServlet,而是匹配到default servlet。例如请求的是/abc/index.jsp,那么服务器会去abc文件夹(与WEB-INF文件夹同级)下查找index.jsp文件,如果请求/WEB-INF/index.jsp,即使存在该jsp,也请求不到任何资源,因为WEB-INF文件夹里的文件是受保护的。
3、如果使用的是/app/*,那么任何不匹配/app/*的url都将匹配到default servlet,服务器会将该url当成是静态资源文件路径去查找相应的文件信息。这估计是最有效处理静态资源的方式了。
最后,附上Servlet和web.xml的代码
import java.io.IOException;
import java.util.Enumeration;
import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyServlet extends HttpServlet {
private static final String TOTAL_REQUEST="totalRequest";
@Override
public void init(ServletConfig config) throws ServletException {
Enumeration<?> names = config.getInitParameterNames();
while(names.hasMoreElements()) {
String key = (String) names.nextElement();
System.out.println(key+":"+config.getInitParameter(key));
}
ServletContext servletContext = config.getServletContext();
//获取InitParameter
String parameter = servletContext.getInitParameter("sql");
System.out.println(parameter);
//设置属性,记录请求数
servletContext.setAttribute(TOTAL_REQUEST, 0);
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
ServletContext servletContext = request.getSession().getServletContext();
//忽略并发代码...
int total =Integer.parseInt(servletContext.getAttribute(TOTAL_REQUEST)+"");
System.out.println("totalRequest:"+(total+1));
servletContext.setAttribute(TOTAL_REQUEST, total+1);
}
@Override
public void destroy() {
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
id="cyt" version="3.0">
<welcomefiles>
<welcomefile>/index.jsp</welcomefile>
</welcomefiles>
<context-param>
<param-name>sql</param-name>
<param-value>mysql</param-value>
</context-param>
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.hxl.MyServlet</servlet-class>
<init-param>
<param-name>name</param-name>
<param-value>hxl</param-value>
</init-param>
<init-param>
<param-name>date</param-name>
<param-value>2018</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 这里不能设置为/*,否则default无效 -->
<servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 默认servlet,用于处理js静态文件
Tomcat, Jetty, JBoss, and GlassFish 自带的默认Servlet的名字:"default"
Google App Engine 自带的 默认Servlet的名字:"_ah_default"
Resin 自带的 默认Servlet的名字:"resin-file"
WebLogic 自带的 默认Servlet的名字:"FileServlet"
WebSphere 自带的 默认Servlet的名字:"SimpleFileServlet"
-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
</servlet-mapping>
</web-app>