Servlet:
Servlet是SUN公司提供的一套规范,名称就叫Servlet规范,它也是JavaEE规范之一。使用JavaEE的API。目前在Oracle官网中的最新版本是JavaEE8,
- Servlet是一个运行在web服务端的java小程序
- 它可以用于接收和响应客户端的请求
- 要想实现Servlet功能,可以实现Servlet接口,继承GenericServlet或者HttpServlet
- 每次请求都会执行service方法
- Servlet还支持配置
Servlet执行过程:
- 客户端浏览器发起请求
- Tomcat服务器解析URL
- 通过URL找到对应的应用
- 通过应用找到web.xml
- 解析请求资源地址URL
- 找到应用的资源
- 执行service方法,响应给客户端浏览器
Servlet关系视图:

Servlet实现方式:
1、 实现Servlet接口,接口中的方法必须全部实现。
表示接口中的所有方法在需求方面都有重写的必要。这种方式支持最大程度的自定义。
2、继承GenericServlet,service方法必须重写,其他方可根据需求,选择性重写。
表示只在接收和响应客户端请求这方面有重写的需求,而其他方法可根据实际需求选择性重写,使开发Servlet变得简单。但是,此种方式是和HTTP协议无关的。
3、继承HttpServlet,它是javax.servlet.http包下的一个抽象类,是GenericServlet的子类。如果选择继承HttpServlet时,只需要重写doGet和doPost方法,不要覆盖service方法。
表示我们的请求和响应需要和HTTP协议相关。也就是说通过HTTP协议来访问的。那么每次请求和响应都符合HTTP协议的规范。请求的方式就是HTTP协议所支持的方式
实现Servlet:
public class ServletDemo03 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 {
System.out.println("实现Servlet方式");
}
@Override
public String getServletInfo() {
return null;
}
@Override
public void destroy() {
}
}
继承GenericServlet:
public class ServletDemo01 extends GenericServlet {
@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service方法执行了...");
}
}
继承HttpServlet::
public class ServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("Servlet执行了");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--Servlet配置-->
<servlet>
<servlet-name>servletDemo</servlet-name>
<servlet-class>com.example.demo.ServletDemo</servlet-class>
</servlet>
<!--映射配置-->
<servlet-mapping>
<servlet-name>servletDemo</servlet-name>
<url-pattern>/servletDemo</url-pattern>
</servlet-mapping>
<!--生命周期配置-->
<servlet>
<servlet-name>servletDemo02</servlet-name>
<servlet-class>com.example.demo.ServletDemo02</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo02</servlet-name>
<url-pattern>/servletDemo02</url-pattern>
</servlet-mapping>
</web-app>
Servlet生命周期:
- 对象的生命周期,就是对象从生到死的过程,
- 出生:请求第一次到达Servlet时,对象就创建出来,并且初始化成功。只出生一次,就放到内存中。
- 活着:服务器提供服务的整个过程中,该对象一直存在,每次都是执行service方法。
- 死亡:当服务停止时,或者服务器宕机时,对象消亡。
- Servlet对象只会创建一次,销毁一次。所以,Servlet对象只有一个实例。如果一个对象实例在应用中是唯一的存在,用了单例模式。
演示:
public class ServletDemo02 extends HttpServlet {
// 对象出生
@Override
public void init() throws ServletException {
System.out.println("出生了");
}
// 对象服务
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("收到客户端请求");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
// 对象死亡
@Override
public void destroy() {
System.out.println("挂了");
}
}
线程安全:
在Servlet中定义了类成员之后,多个浏览器都会共享类成员的数据。其实每一个浏览器端发送请求,就代表是一个线程,那么多个浏览器就是多个线程,多个线程会共享Servlet类成员中的数据,其中任何一个线程修改了数据,都会影响其他线程。因此,所以Servlet它不是线程安全的。分析产生这个问题的根本原因,其实就是因为Servlet是单例,单例对象的类成员只会随类实例化时初始化一次,之后的操作都是改变,而不会重新初始化。
解决:
在Servlet中定义类成员要慎重。如果类成员是共用的,并且只会在初始化时赋值,其余时间都是获取的话,那么是没问题。如果类成员并非共用,或者每次使用都有可能对其赋值,那么就要考虑线程安全问题了,把它定义到doGet或者doPost方法里面去就可以了。或者是直接加synchronized
演示:
public class ServletDemo03 extends HttpServlet {
private String username;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
synchronized (this) {
// 在这里声明的话,就是把成员变量变成局部变量,谷歌进来是一个username,火狐进来又是另一个就不会出现出数据不安全了
// String username = null;
// String getParameter(String name):根据参数名称获取参数值
username = req.getParameter("username");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// resp.getWriter 传数据给前端
PrintWriter writer = resp.getWriter();
writer.println("username:" + username);
writer.close();
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
Servlet映射三种方式:
- 指名道姓,给具体的名称,访问路径必须和配置的一样
- /开头+通配符的方式,只要符合目录结构,无视结尾
- 通配符+固定格式结尾方式,只要符合固定结尾,无视前面路径
说明:映射优先级:越具体优先级越高,1>2>3
演示:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--
<!–ServletHttp配置–>
<servlet>
<servlet-name>servletDemo</servlet-name>
<servlet-class>com.example.demo.ServletDemo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo</servlet-name>
<url-pattern>/servletDemo</url-pattern>
</servlet-mapping>
-->
<!-- <!–/开头+通配符的方式–>
<servlet>
<servlet-name>servletDemo</servlet-name>
<servlet-class>com.example.demo.ServletDemo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>-->
<!--通配符+固定格式结尾 结尾写什么就写什么,开心就好-->
<servlet>
<servlet-name>servletDemo</servlet-name>
<servlet-class>com.example.demo.ServletDemo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo</servlet-name>
<url-pattern>*.itzhuzhu</url-pattern>
</servlet-mapping>
</web-app>
实现类:
public class ServletDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 定义一个商品金额
int money = 1000;
// 获取访问路径
String uri = req.getRequestURI();
// public int lastIndexOf(int ch): 返回指定字符在此字符串中最后一次出现处的索引,如果此字符串中没有这样的字符,则返回 -1。
uri = uri.substring(uri.lastIndexOf("/"));
// 条件判断
if ("/vip".equals(uri)) {
System.out.println("原价:" + money + "尊敬的Vip您的折后价是:" + money * 0.8);
} else if ("/svip".equals(uri)) {
System.out.println("原价:" + money + "尊敬的Sip您的折后价是:" + money * 0.1);
} else {
System.out.println("会员都不开还想打折?打骨折");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
Servlet创建时机:
第一种:服务器加载时创建
优势:
在服务器启动时,就把需要的对象都创建完成了,从而在使用的时候减少了创建对象的时间,提高了首次执行的效率。
弊端:
因为在应用加载时就创建了Servlet对象,因此,导致内存中充斥着大量用不上的Servlet对象,造成了内存的浪费。
第二种:第一次访问时创建
优势:
就是减少了对服务器内存的浪费,因为那些一直没有被访问过的Servlet对象都没有创建,因此也提高了服务器的启动时间。
弊端:
如果有一些要在应用加载时就做的初始化操作,它都没法完成,从而要考虑其他技术实现。
修改创建时机:在标签中添加标签
标签里的数字越小优先级越高,正整数代表服务器加载时创建,负数或不写代表第一次访问的时候创建
<servlet>
<servlet-name>servletDemo</servlet-name>
<servlet-class>com.example.demo.ServletDemo</servlet-class>
<!--数字越小越高,负数或者不写就是第一次访问的时候创建-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo</servlet-name>
<url-pattern>/servletDemo</url-pattern>
</servlet-mapping>
默认Servlet:
默认Servlet是由服务器提供的一个Servlet,它配置在Tomcat的conf目录下web.xml中。它的映射路径是
<url-pattern>/<url-pattern>,在发送请求时,首先会在应用中的web.xml中查找映射配置,找到就执行。找不到对应的Servlet路径时,就去找默认的Servlet,由默认Servlet处理。
ServletConfig:
ServletConfig是Servlet的配置参数对象,在Servlet规范中,允许为每个Servlet都提供一些初始化配置。所以,每个Servlet都一个自己的ServletConfig。它的作用是在Servlet初始化期间,把一些配置信息传递给Servlet。比如servlet是个孩子,ServletConfig就是保姆
生命周期:
由于它是在初始化阶段读取了web.xml中为Servlet准备的初始化配置,并把配置信息传递给Servlet,所以生命周期与Servlet相同。需要注意的是,如果Servlet配置了1,那么ServletConfig也会在应用加载时创建。
配置ServletConfig:
在
<servlet>标签中,通过<init-param>标签配置,有两个子标签
<param-name>:代表初始化参数的key
<param-value>:代表初始化参数的value
ServletConfig方法:
| 返回值 | 方法名 | 说明 |
|---|---|---|
| String | getInitParameter(String name) | 根据参数名称获取参数的值 |
| Enumeration | getInitParameterNames() | 获取所有参数名称的枚举 |
| String | getServletName | 获取Servlet的名称 |
| SerlvetContext | getServletContext | 获取ServletContext对象 |
演示:
配置信息:
<servlet>
<servlet-name>servletDemo05</servlet-name>
<servlet-class>com.example.demo.ServletDemo05</servlet-class>
<!--配置ServletConfig,是以键值对的形式存在的-->
<init-param>
<param-name>itz</param-name>
<param-value>itzhuzhu</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo05</servlet-name>
<url-pattern>/servletDemo05</url-pattern>
</servlet-mapping>
实现类:
public class servletDemo04 extends HttpServlet {
// 声明ServletConfig
private ServletConfig config;
// 因为ServletConfig是在初始化期间传递信息,所以要在init方法里去获取
@Override
public void init(ServletConfig config) throws ServletException {
// 通过init方法,对ServletConfig对象赋值
this.config = config;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// getInitParameter(String name):根据key获取value
String encodingValue = config.getInitParameter("encoding");
System.out.println(encodingValue);
// getInitParameterNames():获取所有key
Enumeration<String> keys = config.getInitParameterNames();
while (keys.hasMoreElements()) {
// 获取每一个key
String key = keys.nextElement();
// 根据key获取value
String value = config.getInitParameter(key);
System.out.println(key + " : " + value);
}
// getServletName 获取Servlet的名称
String servletName = config.getServletName();
System.out.println(servletName);
// getServletContext 获取ServletContext对象
ServletContext servletContext = config.getServletContext();
System.out.println(servletContext);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
ServletContext:
ServletContext对象,它是应用上下文对象。每一个应用有且只有一个ServletContext对象。
作用:
它可以配置和获取应用的全局初始化参数,实现所有Servlet之间的数据共享。
生命周期:
- 出生: 应用一加载,该对象就被创建出来了。一个应用只有一个实例对象。(Servlet和ServletContext都-是单例的)
- 活着:只要应用一直提供服务,对象就一直存在。
- 死亡:应用被卸载(或者服务器挂了),对象死亡。
域对象概念:
- 域对象:指的是对象有作用域(作用范围)
- 域对象的作用:域对象可以实现数据共享。
- 不同作用范围的域对象,共享数据的能力不一样。在Servlet规范中,一共有4个域对象。ServletContext就是其中一个。也是web应用中最大的作用域,叫application域。每个应用只有一个application域(应用域),它可以实现整个应用间的数据共享功能。
ServletContext配置:
ServletContext被称之为应用上下文对象,所以它的配置是针对整个应用的配置,而非某个特定Servlet的配置。它的配置被称为应用的初始化参数配置。
配置的方式,需要在标签中使用来配置初始化参数
<param-name>:全局初始化参数的key
<param-value>:全局初始化参数的value
常用方法:
| 返回值 | 方法名 | 说明 |
|---|---|---|
| String | getInitParameter(String name) | 根据名称获取全局配置的参数 |
| String | getContextPath() | 获取当前应用访问的虚拟目录 |
| String | getRealPath | 根据虚拟目录获取应用部署的磁盘绝对路径 |
应用域常用方法:
| 返回值 | 方法名 | 说明 |
|---|---|---|
| void | setAttribute(String name,Object value) | 向应用域对象中存储数据 |
| Object | getAttribute(String name) | 通过名称获取应用域对象中的数组 |
| void | removeAttribute(String name) | 通过名称移除应用域对象中的数据 |
配置文件:
<!--因为servletContext它不属于某一个servlet,它是配置全局的,所以不能写在servlet里-->
<context-param>
<param-name>desc</param-name>
<param-value>This is Ser vletContextDemo</param-value>
</context-param>
<servlet>
<servlet-name>servletContextDemo</servlet-name>
<servlet-class>com.example.demo.ServletContextDemo</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletContextDemo</servlet-name>
<url-pattern>/servletContextDemo</url-pattern>
</servlet-mapping>
实现类:
public class ServletContextDemo extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 获取ServletContext对象 , GenericServlet底层已经实现了getInitParameter所以可以直接调用 ServletConfig sc = this.getServletConfig();
ServletContext context = getServletContext();
//获取全局配置参数的desc
String value = context.getInitParameter("desc");
System.out.println(value);
//获取应用的访问虚拟目录
String contextPath = context.getContextPath();
System.out.println(contextPath);
//根据虚拟目录获取应用部署的磁盘绝对路径
String realPath = context.getRealPath("/");
System.out.println(realPath);
// 在src、wabapp、webinf创建三个文件获取路径
//获取a.txt文件的绝对路径
String a = context.getRealPath("/WEB-INF/classes/a.txt");
System.out.println(a);
//获取b.txt文件的绝对路径
String b = context.getRealPath("/b.txt");
System.out.println(b);
String c = context.getRealPath("/WEB-INF/c.txt");
System.out.println(c);
//向域对象中存储数据
context.setAttribute("username", "itzhuzhu");
//移除域对象中username的数据,删除了再访问就变成null了
context.removeAttribute("username");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
获取共享数据:
public class ServletDemo05 extends HttpServlet {
// 声明ServletConfig
private ServletConfig config;
// 因为ServletConfig是在初始化期间传递信息,所以要在init方法里去获取
@Override
public void init(ServletConfig config) throws ServletException {
// 通过init方法,对ServletConfig对象赋值
this.config = config;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// getServletContext 获取ServletContext对象
ServletContext servletContext = config.getServletContext();
System.out.println(servletContext);
// getAttribute(String name) 通过名称获取应用域对象中的数组
Object username = servletContext.getAttribute("username");
System.out.println(username);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req, resp);
}
}
Request方法详解:


5365

被折叠的 条评论
为什么被折叠?



