Servlet入门,Servletconfig,Servletcontext,注解配置Servlet


知识点梳理

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_初始化

详细讲义

1 Servlet

1.1 Servlet概述


  • Servlet 是运行在 Java 服务器端的程序,用于接收和响应来自客户端基于 HTTP 协议的请求
  • 如果想实现 Servlet 的功能,可以通过实现 javax.servlet.Servlet 接口或者继承它的实现类
  • 核心方法:service(),任何客户端的请求都会经过该方法

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_java_02




  • Servlet是SUN公司提供的一套规范,名称就叫Servlet规范,它也是JavaEE规范之一
  • 我们可以像学习Java基础一样,通过API来学习Servlet
  • 这里需要注意的是,在我们之前JDK的API中是没有Servlet规范的相关内容,需要使用JavaEE的API
  • 目前在Oracle官网中的最新版本是​​JavaEE8​​,该网址中介绍了JavaEE8的一些新特性
  • 当然,我们可以通过访问​​官方API​​,学习和查阅里面的内容
  • 打开官方API网址,在左上部分找到javax.servlet包,在左下部分找到Servlet,如下图显示:



  • 通过阅读API,我们得到如下信息:
  • 第一:Servlet是一个运行在web服务端的java小程序
  • 第二:它可以用于接收和响应客户端的请求
  • 第三:要想实现Servlet功能,可以实现Servlet接口,继承GenericServlet或者HttpServlet
  • 第四:每次请求都会执行service方法
  • 第五:Servlet还支持配置
  • 具体请看下图:

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_ide_03



1.2 Servlet入门 ***

1.2.1 Servlet快速入门



创建一个 WEB 项目



servlet_demo1





创建一个类继承 GenericServlet



包:com.itheima.servlet



类:ServletDemo1



GenericServlet介绍:implements Servlet,实现了Servlet里的大部分方法,只有一个service方式是抽象的,我们只需要实现这个方法即可

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_ide_04







重写 service 方法

package com.itheima.servlet;

import javax.servlet.GenericServlet;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;

/*
Servlet快速入门1
*/
public class ServletDemo01 extends GenericServlet{

@Override
public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
System.out.println("service方法执行了...");
}
}


service有两个参数,servletRequest,servletResponse ,这俩分别是处理请求和响应的,后续会详细介绍





在 web.xml 中配置 Servlet

<!--配置快速入门1Servlet-->
<servlet>
<servlet-name>servletDemo01</servlet-name>
<servlet-class>com.itheima.servlet.ServletDemo01</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo01</servlet-name>
<url-pattern>/servletDemo01</url-pattern>
</servlet-mapping>


部署并启动项目:配置项目虚拟路径:/demo1



通过浏览器测试

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_初始化_05





分析图:(下图与实际案例有出入,但是我们只需要搞清楚Servlet访问流程即可)



Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_java_06



1.2.2 Servlet执行过程分析


  1. 我们通过浏览器发送请求,请求首先到达Tomcat服务器
  2. 由服务器解析请求URL,然后在部署的应用列表中找到我们的应用
  3. 接下来,在我们的应用中找应用里的web.xml配置文件
  4. 在web.xml中找到Servlet的配置
  5. 找到后执行service方法,最后由ServletDemo1响应客户浏览器

整个过程如下图所示:

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_ide_07



一句话总结执行过程:

浏览器——>Tomcat服务器——>我们的应用——>应用中的web.xml——>Servlet——>响应浏览器

1.2.3 Servlet关系视图


  • 关系视图如下:

  • Servlet,GenericServlet,HTTPServlet,他们都有service方法
  • service方法都有俩参数,ServletRequest,ServletResponse
  • HTTPServlet的参数是HttpServletRequest,HttpServletResponse,他们分别继承自ServletRequest,ServletResponse
  • 这俩参数都是接口,分别处理请求,响应
  • ServletConfig,处理配置
  • ServletContext,处理多个Servlet之间信息共享


1.2.4 Servlet实现方式

1)实现方式说明


  1. 第一种 实现 Servlet 接口,实现所有的抽象方法。该方式支持最大程度的自定义。
  2. 第二种 继承 GenericServlet 抽象类,必须重写 service 方法,其他方法可选择重写。该方式让我们开发 Servlet 变得简单。但是这种方式和 HTTP 协议无关。
  3. 第三种 继承 HttpServlet 抽象类,需要重写 doGet 和 doPost 方法。该方式表示请求和响应都需要和 HTTP 协议相关。


  • 上述前两种都给大家演示过了,我们接下来来试一下第三种

2)继承HttpServlet ***



步骤



创建一个类继承 HttpServlet

//servlet_demo1新建:ServletDemo02.java
package com.itheima.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/*
Servlet快速入门2
*/
public class ServletDemo02 extends HttpServlet {

}


重写 doGet 和 doPost 方法

public class ServletDemo02 extends HttpServlet {

@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 {
//post请求方式一般与get请求方式处理的逻辑一样,所以直接调用doGet
doGet(req,resp);
}
}


在 web.xml 中配置 Servlet

<!--配置快速入门2Servlet-->
<servlet>
<servlet-name>servletDemo02</servlet-name>
<servlet-class>com.itheima.servlet.ServletDemo02</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo02</servlet-name>
<url-pattern>/servletDemo02</url-pattern>
</servlet-mapping>


部署并启动项目



通过浏览器测试

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_ide_08








1.3 Servlet使用细节

1.3.1 Servlet的生命周期



对象的生命周期,就是对象从出生到死亡的过程。即:出生 -> 活着 -> 死亡。官方说法是对象创建到销毁的过程



出生:请求第一次到达 Servlet 时,对象就创建出来,并且初始化成功。只出生(创建)一次,将对象放到内存中



活着:服务器提供服务的整个过程中,该对象一直存在,每次都是执行 service 方法



死亡:当服务停止时,或者服务器宕机时,对象死亡



结论:Servlet 对象只会创建一次,销毁一次。所以 Servlet 对象只有一个实例。如果一个对象实例在应用中是唯一的存在,那么我们就称它为单例模式



代码



创建ServletDemo03

package com.itheima.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*
Servlet生命周期
*/
public class ServletDemo03 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-name>servletDemo03</servlet-name>
<servlet-class>com.itheima.servlet.ServletDemo03</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo03</servlet-name>
<url-pattern>/servletDemo03</url-pattern>
</servlet-mapping>


部署并启动

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_java_09





说明:init只会执行一次





停止服务

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_java_10





停止tomcat,执行destory







1.3.2 Servlet的线程安全 **难点



由于 Servlet 采用的是单例模式,也就是整个应用中只有一个实例对象。所以我们需要分析这个唯一的实例对象中的类成员是否线程安全



模拟用户登录功能来查看 Servlet 线程是否安全



结论:一个浏览器代表一个线程,多个浏览器代表多个线程。按理说我们期望的应该是每个浏览器查看的都应该是自己的用户名。而现在的结果是浏览器中数据混乱。因此,我们可以认为 Servlet 是线程不安全的!



解决:定义类成员要谨慎。如果是共用的,并且只会在初始化时赋值,其他时间都是获取的话,那么是没问题的。如果不是共用的,或者每次使用都有可能对其赋值,那就要考虑线程安全问题了,可以将其定义到 doGet 或 doPost 方法内或者使用同步功能即可。



案例演示



新建ServletDemo4

package com.itheima.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/*
Servlet线程安全
*/
public class ServletDemo04 extends HttpServlet{
//1.定义用户名成员变量
private String username = null;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

//2.获取用户名
username = req.getParameter("username");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}

//3.获取输出流对象
PrintWriter pw = resp.getWriter();

//4.响应给客户端浏览器
pw.print("welcome:" + username);

//5.关流
pw.close();
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}


Servlet配置

<!--演示Servlet线程安全的配置-->
<servlet>
<servlet-name>servletDemo04</servlet-name>
<servlet-class>com.itheima.servlet.ServletDemo04</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo04</servlet-name>
<url-pattern>/servletDemo04</url-pattern>
</servlet-mapping>


演示



因为需要演示线程安全,所以需要两个浏览器模拟两个线程,所以要开两个浏览器



谷歌浏览器中url传递参数username=aaa



火狐浏览器中url传递参数username=bbb



然后谷歌浏览器先访问,紧接着火狐浏览器访问



Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_初始化_11





结果现象是,俩浏览器都是welcome:bbb





分析

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_java_12





解决1:将username由成员变量,放到方法中

package com.itheima.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/*
Servlet线程安全
*/
public class ServletDemo04 extends HttpServlet{
//1.定义用户名成员变量
//private String username = null;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String username = null;//谷歌浏览器进来有一个username,火狐进来也有一个username,所以不会覆盖
//2.获取用户名
username = req.getParameter("username");

try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}

//3.获取输出流对象
PrintWriter pw = resp.getWriter();

//4.响应给客户端浏览器
pw.print("welcome:" + username);

//5.关流
pw.close();
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}


使用同步代码块

package com.itheima.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/*
Servlet线程安全
*/
public class ServletDemo04 extends HttpServlet{
//1.定义用户名成员变量
//private String username = null;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// String username = null;
synchronized (this) { //锁需要唯一,Servlet对象就是唯一的,所以用this
//2.获取用户名
username = req.getParameter("username");

try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}

//3.获取输出流对象
PrintWriter pw = resp.getWriter();

//4.响应给客户端浏览器
pw.print("welcome:" + username);

//5.关流
pw.close();
}
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}




1.3.3 不同映射方式

1)介绍


  1. 第一种 具体名称的方式。访问的资源路径必须和映射配置完全相同
  2. 第二种 / 开头 + 通配符的方式。只要符合目录结构即可,不用考虑结尾是什么
  3. 第三种 通配符 + 固定格式结尾的方式。只要符合固定结尾格式即可,不用考虑前面的路径

注意:优先级问题。越是具体的优先级越高,越是模糊通用的优先级越低。第一种 -> 第二种 -> 第三种

2)第一种:具体名称的方式 (精准匹配)***



此种方式,只有和映射配置一模一样时,Servlet才会接收和响应来自客户端的请求。



例如:映射为:/servletDemo5



访问URL:​​http://localhost:8080/demo1/servletDemo5​​​



新建ServletDemo5

package com.itheima.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*
Servlet不同映射方式
*/
public class ServletDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("ServletDemo05执行了...");
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}


配置Servlet

<!--演示Servlet不同映射方式-->
<!--具体名称的方式-->
<servlet>
<servlet-name>servletDemo05</servlet-name>
<servlet-class>com.itheima.servlet.ServletDemo05</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo05</servlet-name>
<url-pattern>/servletDemo05</url-pattern>
</servlet-mapping>


访问:



Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_java_13



3)第二种:/开头+通配符的方式

我们还是使用ServletDemo5,只需要修改配置即可(把上一个具体名称的配置屏蔽掉)

<!--/开头+通配符的方式-->
<servlet>
<servlet-name>servletDemo05</servlet-name>
<servlet-class>com.itheima.servlet.ServletDemo05</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo05</servlet-name>
<url-pattern>/servlet/*</url-pattern>
</servlet-mapping>


访问

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_初始化_14





4) 第三种:通配符+固定格式结尾



此种方式,只要符合固定结尾格式即可,其前面的访问URI无须关心(注意协议,主机和端口必须正确)



例如:映射为:*.do



访问URL:​​http://localhost:8080/demo1/aaa.do​​​

​http://localhost:8080/demo1/bbb.do​​​



这两个URL都可以方法。因为都是以.do作为结尾,而前面用*号通配符配置的映射,所有无须关心。



依然使用ServletDemo5,修改配置即可

<!--通配符+固定格式结尾的方式-->
<servlet>2
<servlet-name>servletDemo05</servlet-name>
<servlet-class>com.itheima.servlet.ServletDemo05</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo05</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>


访问

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_java_15






1.3.4 Servlet多映射的使用场景



我们可以给一个 Servlet 配置多个访问映射,从而根据不同的请求路径来实现不同的功能



场景分析:



如果访问的资源路径是 /vip 商品价格打9折



如果访问的资源路径是 /vvip 商品价格打5折



如果访问的资源路径是其他 商品价格不打折





案例:新建ServletDemo6

package com.itheima.servlet;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/*
Servlet 多路径映射
*/
public class ServletDemo06 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1. 定义一个商品金额
int money = 1000;

//2. 获取访问的资源路径
String name = req.getRequestURI();
name = name.substring(name.lastIndexOf("/"));

//3. 条件判断
if("/vip".equals(name)) {
//如果访问资源路径是/vip 商品价格为9折
System.out.println("商品原价为:" + money + "。优惠后是:" + (money*0.9));
} else if("/vvip".equals(name)) {
//如果访问资源路径是/vvip 商品价格为5折
System.out.println("商品原价为:" + money + "。优惠后是:" + (money*0.5));
} else {
//如果访问资源路径是其他 商品价格原样显示
System.out.println("商品价格为:" + money);
}
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}


配置Servlet

<!--演示Servlet多路径映射-->
<servlet>
<servlet-name>servletDemo06</servlet-name>
<servlet-class>com.itheima.servlet.ServletDemo06</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>servletDemo06</servlet-name>
<url-pattern>/itheima/*</url-pattern>
</servlet-mapping>


访问

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_ide_16





1.3.5 Servlet创建时机


  1. 第一次访问时创建

优势:减少对服务器内存的浪费。提高了服务器启动的效率

弊端:如果有一些要在应用加载时就做的初始化操作,无法完成


  1. 服务器加载时创建

优势:提前创建好对象,提高了首次执行的效率。可以完成一些应用加载时要做的初始化操作

弊端:对服务器内存占用较多,影响了服务器启动的效率



  • 修改 Servlet 创建时机。在​​<servlet>​​标签中,添加​​<load-on-startup>​​标签。

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_java_17




  • 正整数代表服务器加载时创建,值越小、优先级越高。 负整数或不写代表第一次访问时创建

  • <load-on-startup>加载顺序的序号</load-on-startup>
  • 序号为1,就是服务器启动时第一个加载
  • 序号为2,就是服务器启动时第二个加载

  • 如果两个Servlet都要配置为正整数,那么值小的优先级高
  • 配置:修改ServletDemo3的配置,增加load-on-startup
    <!--演示Servlet生命周期的配置--> <servlet> <servlet-name>servletDemo03</servlet-name> <servlet-class>com.itheima.servlet.ServletDemo03</servlet-class> <!--配置Servlet启动时机 正整数代表服务器启动时创建,负数或不写代表第一次访问时创建--> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>servletDemo03</servlet-name> <url-pattern>/servletDemo03</url-pattern> </servlet-mapping>
  • 效果:如果不配置,是在访问ServletDemo3 的时候初始化,如果配置,那就是在启动tomcat的时候初始化

  • 下图是配置后,启动tomcat打印的


1.3.6 默认Servlet


  • 默认Servlet是由服务器提供的一个Servlet,它配置在Tomcat的conf目录下的web.xml中。如下图所示:

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_ide_18




  • 它的映射路径是​​<url-pattern>/<url-pattern>​​,我们在发送请求时,首先会在我们项目中的 web.xml 中查找映射配置,找到则执行
  • 但是当找不到对应的 Servlet 路径时,就去找默认的 Servlet,由默认 Servlet 处理。所以,一切都是 Servlet。
  • 访问一个不存在的url

  • 这个404界面,其实就是tomcat配置的默认的Servlet处理的结果


2 ServletConfig

2.1 ServletConfig介绍

2.1.1 基本概念 ***


  • ServletConfig 是 Servlet 的配置参数对象,在 Servlet 的规范中,允许为每一个 Servlet 都提供一些初始化的配置。所以,每个 Servlet 都有一个自己的 ServletConfig
  • 作用:在 Servlet 的初始化时,把一些配置信息传递给 Servlet

2.1.2 生命周期


  • 生命周期:和 Servlet 相同
  • 由于它是在初始化阶段读取了web.xml中为Servlet准备的初始化配置,并把配置信息传递给Servlet,所以生命周期与Servlet相同
  • 这里需要注意的是,如果Servlet配置了​​<load-on-startup>1</load-on-startup>​​,那么ServletConfig也会在应用加载时创建

Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_ide_19




  • ServletConfig的配置信息都是键值对的形式

2.2 ServletConfig的使用

2.2.1 配置方式


  • 在​​<servlet>​​标签中,通过​​<init-param>​​标签来配置。有两个子标签。
  • ​<param-name>​​:代表初始化参数的 key。
  • ​<param-value>​​:代表初始化参数的 value。

  • 一个init-param配置一个信息,一个信息由name和value组成

  • 案例

  1. 新建项目:servlet_demo2
  2. src中新建包:com.itheima.servlet
  3. 新建类:ServletConfigDemo
    package com.itheima.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /* ServletConfig的使用 */ public class ServletConfigDemo extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
  4. 配置Servlet
    <!--配置Servlet--> <servlet> <servlet-name>servletConfigDemo</servlet-name> <servlet-class>com.itheima.servlet.ServletConfigDemo</servlet-class> <!--配置ServletConfig初始化参数--> <init-param> <!--用于获取初始化参数的key--> <param-name>encoding</param-name> <!--初始化参数的值--> <param-value>UTF-8</param-value> </init-param> <init-param> <param-name>desc</param-name> <param-value>This is ServletConfig</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>servletConfigDemo</servlet-name> <url-pattern>/servletConfigDemo</url-pattern> </servlet-mapping>


2.2.2 常用方法


  • 常用方法:

  • 掌握getInitParameter()方法

  • 代码:接着在ServletConfigDemo中写代码:
    public class ServletConfigDemo extends HttpServlet { //声明ServletConfig配置对象 private ServletConfig config; /* 通过init方法来为ServletConfig配置对象赋值 */ @Override public void init(ServletConfig config) throws ServletException { this.config = config; } @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //根据key获取value String encodingValue = config.getInitParameter("encoding"); System.out.println(encodingValue); //获取Servlet的名称 String servletName = config.getServletName(); System.out.println(servletName); //获取所有的key Enumeration<String> names = config.getInitParameterNames(); //遍历得到的key while(names.hasMoreElements()) { //获取每一个key String name = names.nextElement(); //通过key获取value String value = config.getInitParameter(name); System.out.println("name:" + name + ",value:" + value); } //获取ServletContext对象 ServletContext context = config.getServletContext(); System.out.println(context); //获取ServletContextDemo设置共享的数据 , 这个是在写下个案例的时候添加的 //Object username = context.getAttribute("username"); //System.out.println(username); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
  • 部署项目,配置虚拟目录为demo2,启动tomcat
  • 效果
    Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_ide_20
     
     


3 ServletContext

3.1 ServletContext介绍

3.1.1 基本介绍 ***


  • ServletContext 是应用上下文对象每一个应用中只有一个 ServletContext 对象
  • 作用:可以获得应用的全局初始化参数和达到 Servlet 之间的数据共享。
  • 上下文理解:环境,不同环境给我们带来的信息是不一样的。所以环境中有很多信息,数据,也就是环境是用于存储数据的。
  • 生命周期:应用一加载则创建,应用被停止则销毁。

  • 出生——活着——死亡
  • 出生: 应用一加载,该对象就被创建出来了。一个应用只有一个实例对象。(Servlet和ServletContext都是单例的)
  • 活着:只要应用一直提供服务,该对象就一直存在。
  • 死亡:应用被卸载(或者服务器挂了),该对象消亡。

  • ServletContext图示:
    Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_初始化_21
     
     

3.1.2 域对象概念 ***


  • 域对象指的是对象有作用域也就是有作用范围
  • 域对象可以实现数据的共享
  • 不同作用范围的域对象,共享数据的能力也不一样
  • 在 Servlet 规范中,一共有 4 个域对象

  • ServletContext 就是其中的一个
  • 它也是 web 应用中最大的作用域,也叫 application 域

  • 在整个项目范围都可以使用 应用域共享的数据

  • 它可以实现整个应用之内的数据共享
  • ServletContext是一个接口,程序运行起来之后打印ServletContext的实例对象,其实是一个ApplicationContextFacade对象

  • ApplicationContextFacade是ServletContext的实现类



3.2 ServletContext的使用

3.2.1 配置方式


  • ServletContext 并不属于某个 Servlet 的配置,而是针对于整个应用的配置,也叫全局的初始化参数
  • 在​​<web-app>​​标签中,通过​​<context-param>​​标签来配置。有两个子标签
  • ​<param-name>​​:代表全局初始化参数的 key
  • ​<param-value>​​:代表全局初始化参数的 value
    Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_java_22
     
     
  • 案例:新建ServletContextDemo
    package com.itheima.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; public class ServletContextDemo extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
  • 配置Servlet,并且配置ServletContext<web-app> .... <!--配置Servlet--> <servlet> <servlet-name>servletContextDemo</servlet-name> <servlet-class>com.itheima.servlet.ServletContextDemo</servlet-class> </servlet> <servlet-mapping> <servlet-name>servletContextDemo</servlet-name> <url-pattern>/servletContextDemo</url-pattern> </servlet-mapping> <!--配置ServletContext--> <context-param> <param-name>globalEncoding</param-name> <param-value>UTF-8</param-value> </context-param> <context-param> <param-name>globalDesc</param-name> <param-value>This is ServletContext</param-value> </context-param> </web-app>

  • 注意ServletContext的配置是在wep-app节点下,与servlet配置同级别


3.2.2 常用方法1 ***


  • 常用方法

  • 掌握:getContextPath和getRealPath

  • 准备工作:新建三个空的txt文件,如下
    Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_ide_23
     
     
  • 代码:继续在ServletContextDemo中写:
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取ServletContext对象 ServletContext context = getServletContext(); //获取全局配置的globalEncoding String value = context.getInitParameter("globalDesc"); System.out.println(value); //获取应用的访问虚拟目录 String contextPath = context.getContextPath(); System.out.println(contextPath); //根据虚拟目录获取应用部署的磁盘绝对路径 String realPath = context.getRealPath("/"); System.out.println(realPath); //获取b.txt文件的绝对路径 String b = context.getRealPath("/b.txt"); System.out.println(b); //获取c.txt文件的绝对路径 String c = context.getRealPath("/WEB-INF/c.txt"); System.out.println(c); //获取a.txt文件的绝对路径 String a = context.getRealPath("/WEB-INF/classes/a.txt"); System.out.println(a); }
  • 效果

  • context.getRealPath("/");获取到的就是当前项目发布的路径


3.2.3 常用方法2 ***


  • 常用方法2
    Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_ide_24
     
     
  • 代码:
    //修改ServletContextDemo:存储数据 //向域对象中存储数据 context.setAttribute("username","zhangsan"); //修改ServletConfigDemo:获取数据 //获取ServletContextDemo设置共享的数据 Object username = context.getAttribute("username"); System.out.println(username);
  • 效果

  • 先访问contextdemo存储数据
  • 再访问configdemo获取数据


4 注解开发Servlet

4.1 Servlet3.0规范


  • 我们使用的是 Tomcat 9 版本。JavaEE 规范要求是 8 。对应的 Servlet 版本应该是 4.x 版本。但是,在企业开发中,稳定要远比追求新版本要重要。所以我们会降版本使用,用的是 Servlet 3.1 版本
  • 其实我们之前的操作全都是基于 Servlet 2.5 版本规范的,也就是借助于配置文件的方式。后来随着软件开发逐步的演变,基于注解的配置开始流行。而 Servlet 3.0 版本也就开始支持注解开发了
  • Servlet 3.0 版本既保留了 2.5 版本的配置方式,同时又支持了全新的注解配置方式。它可以完全不需要 web.xml 配置文件,就能实现 Servlet 的配置,同时还有一些其他的新特性,我们在后面的课程中会慢慢学习到
    Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_初始化_25
     
     
  • 总结:

  • 之前基于配置文件方式,这个是Servlet2.5版本的规范
  • 但是每添加一个Servlet,就需要自己配置一个,感觉有点繁琐
  • Servlet3.0就出现了注解方式,可以省去配置


4.2 注解开发

4.2.1 自动注解开发Servlet ***


  1. 新建项目:servlet_demo3
  2. 配置Java EE8

  • 剩余两步省略

  1. 新建之后,项目目录如下

  • web下没有WEB-INF了,web.xml也没有了

  • 虽然web.xml不用了,但是WEB-INF还需要,所以WEB-INF需要自己创建出来

  • index.jsp没用,删除即可

  1. 新建类:com.itheima.servlet.ServletDemo1
    package com.itheima.servlet; import javax.servlet.ServletException; import javax.servlet.annotation.WebServlet; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /* 自动注解配置Servlet @WebServlet("Servlet路径") */ @WebServlet("/servletDemo1") public class ServletDemo1 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); } }
  2. 可以正常访问

4.2.2 注解详解


  • 注解详解
    Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_初始化_26
     
     

4.2.3 手动创建容器(了解)

1)前置说明


  • Servlet 3.0 规范除了使用自动注解的配置方式外,还支持手动创建 Servlet 容器的方式
  • 如果使用必须遵循其编写规范。在 3.0 版本加入了一个新的接口:
    Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_java_27
     
     

2)编写步骤


  1. 定义一个类ServletDemo2,继承 HttpServlet
  2. 重写 doGet 和 doPost 方法
    package com.itheima.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; /* 手动创建容器配置Servlet */ public class ServletDemo2 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); } }
  3. 定义一个类,实现 ServletContainerInitializer 接口
    package com.itheima.servlet; import javax.servlet.ServletContainerInitializer; import javax.servlet.ServletContext; import javax.servlet.ServletRegistration; import java.util.Set; /* 注册配置Servlet的功能类 */ public class MyRegister implements ServletContainerInitializer { @Override public void onStartup(Set<Class<?>> set, ServletContext servletContext) { //完成Servlet的创建和配置 } }
  4. 在 src 目录下创建一个 META-INF 的
  5. 在 META-INF 包下创建一个 services 的包
  6. 在 services 包下创建一个 javax.servlet.ServletContainerInitializer 的文件
  7. 文件中的内容为容器实现类的全类名
    com.itheima.servlet.MyRegister
  8. 在容器实现类中的 onStartup 方法中完成注册 Servlet
    public void onStartup(Set<Class<?>> set, ServletContext servletContext) { //完成Servlet的创建和配置 //1.创建Servlet对象 ServletDemo2 servletDemo2 = new ServletDemo2(); //2.在ServletContext中添加Servlet,并得到Servlet的配置对象 ServletRegistration.Dynamic registration = servletContext.addServlet("servletDemo2", servletDemo2); //3.配置Servlet registration.setLoadOnStartup(0); //Servlet加载时机 registration.addMapping("/servletDemo2"); //映射访问资源路径 }
  9. 部署并启动项目
  10. 通过浏览器测试
    Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_初始化_28
     
     

5 Servlet应用案例-学生管理系统 ***

5.1 案例效果介绍


  • 效果

  • 访问案例首页,看到一个可以保存学生信息的界面
  • 输入内容,点击保存,通过java服务器,然后最终保存到txt中
  • 最后java服务器返回成功结果


5.2 案例实现


  1. 创建一个 web 项目:servlet_test,配置虚拟目录/stu

  • 选择javaee 7,这里我们还是用web.xml

  1. 创建一个用于保存学生信息的 html 文件:web下新建addStudent.html
    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>添加学生</title> </head> <body> -- ?username=张三&age=18&score=718 <form action="/stu/studentServlet" method="get" autocomplete="off"> 学生姓名:<input type="text" name="username"> <br/> 学生年龄:<input type="number" name="age"> <br/> 学生成绩:<input type="number" name="score"> <br/> <button type="submit">保存</button> </form> </body> </html>
  2. 创建一个类com.itheima.servlet.StudentServlet,继承 HttpServlet
  3. 重写 doGet 和 doPost 方法
    package com.itheima.servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.BufferedWriter; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; public class StudentServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doGet(req,resp); } }
  4. 在 web.xml 文件中修改默认主页和配置 Servlet
    <!--修改默认主页--> <welcome-file-list> <welcome-file>/addStudent.html</welcome-file> </welcome-file-list> <!--配置Servlet--> <servlet> <servlet-name>studentServlet</servlet-name> <servlet-class>com.itheima.servlet.StudentServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>studentServlet</servlet-name> <url-pattern>/studentServlet</url-pattern> </servlet-mapping>
  5. 在 doGet 方法中接收表单数据保存到文件中,并响应给浏览器结果
    // -- ?username=张三&age=18&score=718 protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { //获取表单中的数据 String username = req.getParameter("username"); // 获取url后边的?的参数 String age = req.getParameter("age"); String score = req.getParameter("score"); //将数据保存到stu.txt文件中 BufferedWriter bw = new BufferedWriter(new FileWriter("d:\\stu.txt",true)); bw.write(username + "," + age + "," + score); bw.newLine(); bw.close(); //给浏览器回应 PrintWriter pw = resp.getWriter(); pw.println("Save Success~"); pw.close(); }
  6. 部署并启动项目
  7. 通过浏览器测试
     Java-web核心:Servlet入门,Servletconfig,Servletcontext,注解配置Servlet_初始化_29