JavaWeb 的三大组件:Servlet 程序、Filter 过滤器、Listener 监听器

1 Servlet技术

1.1 Servlet简介

Servlet(Server Applet),全称Java Servlet,未有中文译文。是用Java语言编写的服务器端程序。其主要功能在于交互式地浏览和修改数据,生成动态Web内容。狭义的Servlet是指Java语言实现的一个接口,广义的Servlet是指任何实现了这个Servlet接口的类,一般情况下,人们将Servlet理解为后者。特定如下:

  1. Servlet是JavaEE规范之一。
  2. Servlet是JavaWeb三大组件之一。三大组件分别是:Servlet程序、Filter过滤器、Listener监听器。
  3. Servlet是运行在服务器上的一个程序,用于接收和相应客户端的请求。

1.2 Servlet API概述

Servlet API 包含以下4个Java包:

  • javax.servlet 其中包含定义servlet和servlet容器之间契约的类和接口。
  • javax.servlet.http 其中包含定义HTTP Servlet 和Servlet容器之间的关系。
  • javax.servlet.annotation 其中包含标注servlet,Filter,Listener的标注。它还为被标注元件定义元数据。
  • javax.servlet.descriptor,其中包含提供程序化登录Web应用程序的配置信息的类型。

1.3 实现Servlet程序

步骤:

  1. 自定义一个类,并实现Servlet接口。
  2. 实现service方法,用于处理和相应数据。
  3. 在web.xml中配置servlet程序的访问地址。

代码:

  • 自定义类

    public class Demo01FirstServlet implements Servlet {
    /**
    	service方法是用来处理请求和相应的
    */
        @Override
        public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
            System.out.println("Demo01FirstServlet 被访问了。");
        }
    }
    
  • 配置方法

    <url-pattern>标签配置方式:
    	1.完全匹配: /demo03
    	2.目录名匹配: /aaa/bbb/*(localhost:8080/day23/aaa/bbb/任意内容 即正确)
    	3.后缀名匹配:  *.abc (URL:localhost:8080/day23/任意.abc 即正确)
    	4.缺省匹配  以上三种路径都没有匹配成功的时候 默认执行缺省匹配  语法: /(在Tomcat中的webxml全局配置文件中默认自带缺省匹配)
    	注意:
    		目录名匹配和后缀名匹配不能一起使用,会有冲突
        	eg:/aaa/bbb/*.abc	   
    
  • 配置文件

    <?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标签给Tomcat配置Servlet程序 -->
        <servlet>
            <!-- servlet-name 标签给Servlet程序起一个别名 -->
            <servlet-name>FirstServlet</servlet-name>
            <!-- servlet-class 标签是Servlet程序的全类名 -->
            <servlet-class>demo01FirstServlet.Demo01FirstServlet</servlet-class>
        </servlet>
        <!-- 给Servlet配置详细信息 -->
        <servlet-mapping>
            <!-- servlet-name 表明是给哪一个Servlet程序配置的 -->
            <servlet-name>FirstServlet</servlet-name>
            <!-- url-pattern 标签配置访问地址 -->
            <!-- / 斜杠在服务器解析的时候,表示地址为:http://ip:port/工程路径 -->
            <!-- /hello 表示地址为:http://ip:port/工程路径/hello -->
            <url-pattern>/FirstServlet</url-pattern>
        </servlet-mapping>
    </web-app>
    

1.4 访问Servlet程序

图解
在这里插入图片描述

1.5 Tomcat中的web.xml全局配置文件

文件路径:apache-tomcat-8.0.50\conf\web.xml

<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>
	<!-- The mapping for the default servlet -->
	<!-- 
		默认的Servlet
		<url-pattern>/</url-pattern>:缺省匹配
		前端访问后台的Servlet,没有匹配上对应的Servlet,就会访问默认的Servlet
		eg:找不到指定的资源则返回404
	-->
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

	<!-- 
 		Tomcat支持javaEE13规范中的两个:Servlet,jsp
		jsp的本质就是一个Serlvet
	-->
	<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>jsp</servlet-name>
        <url-pattern>*.jsp</url-pattern>
        <url-pattern>*.jspx</url-pattern>
    </servlet-mapping>	
	
    
    <!--
		配置的session的超时访问时间,默认是30分钟,时间单位默认是分钟
		登录的时候:输入用户名和密码,会把用户名和密码存储到session对象中
		如果我们不访问页面,默认存储用户名和密码的时间就是30分钟
	-->
    <session-config>
        <session-timeout>30</session-timeout>
    </session-config>
    
    <!--
		<extension>avi</extension>:视频文件的扩展名是.avi,是人看的,浏览器并不认识
		<mime-type>video/x-msvideo</mime-type>:视频的文件类型,给浏览器识别的,浏览器遇到video/x-msvideo类型,就会把这个文件以视频播放
	-->
    <mime-mapping>
        <extension>avi</extension>
        <mime-type>video/x-msvideo</mime-type>
    </mime-mapping>

	<mime-mapping>
        <extension>txt</extension>
        <mime-type>text/plain</mime-type>
    </mime-mapping>

	<mime-mapping>
        <extension>jpg</extension>
        <mime-type>image/jpeg</mime-type>
    </mime-mapping>
    
    <!--
		默认欢迎页面的列表
		访问项目 http://localhost:8080/day23 默认打开欢迎页面
	-->
    <welcome-file-list>
        <welcome-file>index.html</welcome-file>
        <welcome-file>index.htm</welcome-file>
        <welcome-file>index.jsp</welcome-file>
    </welcome-file-list>

1.6 Servlet中的方法和其声明周期

1.6.1 相关方法

/**
	Servlet接口
*/
public interface Servlet {
    /*
    	当Servlet第一次被请求时,Servlet容器就会开始调用这个方法来初始化一个Servlet对象出来,但是		这个方法只会在第一次被请求时调用一次。
    */
    void init(ServletConfig var1) throws ServletException;
    
	/*
		这个方法会返回由Servlet容器传给init( )方法的ServletConfig对象。
	*/
    ServletConfig getServletConfig();
    
	/*
		每当请求Servlet时,Servlet容器就会调用这个方法。用来处理请求和相应信息。
	*/
    void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;
    
	/*
		这个方法会返回Servlet的一段描述,可以返回一段字符串。
	*/
    String getServletInfo();
    
	/*
		当要销毁Servlet时,Servlet容器就会调用这个方法,一般用作一些资源清理的工作
	*/
    void destroy();
}

1.6.2 生命周期

​ init( ),service( ),destroy( )是Servlet生命周期的方法。代表了Servlet从“出生”到“工作”再到“死亡 ”的过程。Servlet容器(例如TomCat)会根据下面的规则来调用这三个方法:

  1. init( ),当Servlet第一次被请求时,Servlet容器就会开始调用这个方法来初始化一个Servlet对象出来,但是这个方法在后续请求中不会在被Servlet容器调用,就像人只能“出生”一次一样。我们可以利用init( )方法来执行相应的初始化工作。调用这个方法时,Servlet容器会传入一个ServletConfig对象进来从而对Servlet对象进行初始化。
  2. service( )方法,每当请求Servlet时,Servlet容器就会调用这个方法。就像人一样,需要不停的接受老板的指令并且“工作”。第一次请求时,Servlet容器会先调用init( )方法初始化一个Servlet对象出来,然后会调用它的service( )方法进行工作,但在后续的请求中,Servlet容器只会调用service方法了。
  3. destory,当要销毁Servlet时,Servlet容器就会调用这个方法,就如人一样,到时期了就得死亡。在卸载应用程序或者关闭Servlet容器时,就会发生这种情况,一般在这个方法中会写一些清除代码。

1.7 GenericServlet抽象类

​ 前面我们编写Servlet一直是通过实现Servlet接口来编写的,但是,使用这种方法,则必须要实现Servlet接口中定义的所有的方法,即使有一些方法中没有任何东西也要去实现,并且还需要自己手动的维护ServletConfig这个对象的引用。因此,这样去实现Servlet是比较麻烦的。

​ GenericServlet抽象类的出现很好的解决了这个问题。本着尽可能使代码简洁的原则,GenericServlet实现了Servlet和ServletConfig接口,下面是GenericServlet抽象类的具体代码:

public abstract class GenericServlet implements Servlet, ServletConfig, Serializable {
    private static final String LSTRING_FILE = "javax.servlet.LocalStrings";
    private static ResourceBundle lStrings = 	
        		ResourceBundle.getBundle("javax.servlet.LocalStrings");
    private transient ServletConfig config;

	public GenericServlet() {
	}
	 
	public void destroy() {
	}
	 
	public String getInitParameter(String name) {
	    ServletConfig sc = this.getServletConfig();
	    if (sc == null) {
	        throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
	    } else {
	        return sc.getInitParameter(name);
	    }
	}
	 
	public Enumeration<String> getInitParameterNames() {
	    ServletConfig sc = this.getServletConfig();
	    if (sc == null) {
	        throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
	    } else {
	        return sc.getInitParameterNames();
	    }
	}
	 
	public ServletConfig getServletConfig() {
	    return this.config;
	}
	 
	public ServletContext getServletContext() {
	    ServletConfig sc = this.getServletConfig();
	    if (sc == null) {
	        throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
	    } else {
	        return sc.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() {
	    ServletConfig sc = this.getServletConfig();
	    if (sc == null) {
	        throw new IllegalStateException(lStrings.getString("err.servlet_config_not_initialized"));
	    } else {
	        return sc.getServletName();
	    }
	}
}

​ 从源码中可以看出,GenericServlet抽象类对Servlet接口中的一些方法提供了一些默认实现,所以使用GenericServlet抽象类有下面几个优点:

  1. 为Servlet接口中的所有方法提供了默认的实现,则程序员需要什么就直接改什么,不再需要把所有的方法都自己实现了。
  2. 提供方法,包括ServletConfig对象中的方法。
  3. 将init( )方法中的ServletConfig参数赋给了一个内部的ServletConfig引用从而来保存ServletConfig对象,不需要程序员自己去维护ServletConfig了。

​ 但是,在源码中除了维护ServletConfig的Init(ServletConfig config)方法外还有一个空参的Init()方法,那么这个空参构造方法的作用是什么呢?

​ 我们知道,抽象类是无法直接产生实例的,需要另一个类去继承这个抽象类,那么就会发生方法覆盖的问题,如果在类中覆盖了GenericServlet抽象类的init()方法,那么程序员就必须手动的去维护ServletConfig对象了,还得调用super.init(servletConfig)方法去调用父类GenericServlet的初始化方法来保存ServletConfig对象,这样会给程序员带来很大的麻烦。GenericServlet提供的第二个不带参数的init( )方法,就是为了解决上述问题的。

​ 这个不带参数的init()方法,是在ServletConfig对象被赋给ServletConfig引用后,由第一个带参数的init(ServletConfig servletconfig)方法调用的,那么这意味着,当程序员如果需要覆盖这个GenericServlet的初始化方法,则只需要覆盖那个不带参数的init( )方法就好了,此时,servletConfig对象仍然有GenericServlet保存着。

1.8 HttpServlet抽象类

​ HttpServlet是由GenericServlet抽象类扩展而来的,HttpServlet之所以运用广泛的另一个原因是现在大部分的应用程序都要与HTTP结合起来使用。这意味着我们可以利用HTTP的特性完成更多更强大的任务。

​ Javax.servlet.http包是ServletAPI中的第二个包,其中包含了用于编写Servlet应用程序的类和接口。

​ Javax.servlet.http中的许多类型都覆盖了Javax.servlet中的类型。如图:

1.8.1 基础HttpServlet实现Servlet程序

步骤:

  1. 自定义类继承HttpServlet类。
  2. 根据需求重写doGet和doPost方法。
  3. 在web.xml中配置Servlet程序的访问地址。

代码

public class Demo02HttpServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Demo02HttpServlet的doGet方法");
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Demo02HttpServlet的doPost方法");
    }
}

配置文件

<servlet>
        <servlet-name>HttpServlet</servlet-name>
        <servlet-class>demo02HttpServlet.Demo02HttpServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>HttpServlet</servlet-name>
        <url-pattern>/HttpServlet</url-pattern>
    </servlet-mapping>

1.8.2 源码分析

​ HttpServlet抽象类覆盖了GenericServlet抽象类中的Service( )方法,并且添加了一个自己独有的Service(HttpServletRequest request,HttpServletResponse方法。

​ 首先来看GenericServlet抽象类中是如何定义service方法的:

public abstract void service(ServletRequest var1, ServletResponse var2) throws ServletException, IOException;

​ GenericServlet抽象类中的service方法也是一个抽象方法,需要子类自己定义。在HttpServlet中service方法:

public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
    HttpServletRequest request;
    HttpServletResponse response;
    try {
        request = (HttpServletRequest)req;
        response = (HttpServletResponse)res;
    } catch (ClassCastException var6) {
        throw new ServletException("non-HTTP request or response");
    }
 
    this.service(request, response);
}

​ 我们发现,HttpServlet中的service方法把接收到的ServletRequsest类型的对象转换成了HttpServletRequest类型的对象,把ServletResponse类型的对象转换成了HttpServletResponse类型的对象。之所以能够这样强制的转换,是因为在调用Servlet的Service方法时,Servlet容器总会传入一个HttpServletRequest对象和HttpServletResponse对象,预备使用HTTP。因此,转换类型当然不会出错了。

​ 转换之后,service方法把两个转换后的对象传入了另一个service方法,那么我们再来看看这个方法是如何实现的:

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 = req.getDateHeader("If-Modified-Since");
            if (ifModifiedSince < lastModified) {
                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);
    }
 
}

​ 这个service方法的参数是HttpServletRequest对象和HttpServletResponse对象,刚好接收了上一个service方法传过来的两个对象。在service方法中还是没有任何的服务逻辑,但是却在解析HttpServletRequest中的方法参数,并调用以下方法之一:doGet,doPost,doHead,doPut,doTrace,doOptions和doDelete。这7种方法中,每一种方法都表示一个Http方法。doGet和doPost是最常用的。所以,如果我们需要实现具体的服务逻辑,不再需要覆盖service方法了,只需要覆盖doGet或者doPost就好了。

​ 而HttpServlet有两个特性是GenericServlet所不具备的:

  1. 不用覆盖service方法,而是覆盖doGet或者doPost方法。在少数情况,还会覆盖其他的5个方法。
  2. 使用的是HttpServletRequest和HttpServletResponse对象。

1.9 使用IDEA创建Servlet程序

在这里插入图片描述
在这里插入图片描述

优点:会自动补全一部分代码以及部分配置文件。

代码

public class Demo03IdeaServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

配置文件

<!-- IDEA自动完成servlet标签 -->
    <servlet>
        <servlet-name>IdeaServlet</servlet-name>
        <servlet-class>demo03IdeaServlet.Demo03IdeaServlet</servlet-class>
    </servlet>
    <!-- 自己配置servlet-mapping标签内容 -->
    <servlet-mapping>
        <servlet-name>IdeaServlet</servlet-name>
        <url-pattern>/IdeaServlet</url-pattern>
    </servlet-mapping>

注意:为了使用方便,我们一般把参数设置为req和resp,但是自动生成的可能是request和response,我们可以对自动生成的模板进行设置:
在这里插入图片描述

2 ServletConfig类

​ ServletConfig是Servlet 程序的配置信息类。

​ 当Servlet容器初始化Servlet时,Servlet容器会给Servlet的init( )方式传入一个ServletConfig对象。仅仅在初次创建时创建一个,及整个Servlet生命周期内公用。

其中几个方法如下:

public interface ServletConfig {
    String getServletName();//获取在web.xml配置文件中的name(虚拟名)值

    ServletContext getServletContext();//获取Context对象

    String getInitParameter(String var1);//获取初始化参数

    Enumeration<String> getInitParameterNames();//获取所有的初始化名称
}

2.1 ServletConfig 类的三大作用

  1. 可以获取 Servlet 程序的别名 servlet-name 的值
  2. 获取初始化参数 init-param
  3. 获取 ServletContext 对象

配置文件

<!-- Servlet标签给Tomcat配置Servlet程序 -->
<servlet>
    <!-- servlet-name 标签给Servlet程序起一个别名 -->
    <servlet-name>FirstServlet</servlet-name>
    <!-- servlet-class 标签是Servlet程序的全类名 -->
    <servlet-class>demo01FirstServlet.Demo01FirstServlet</servlet-class>
    
    <!--使用init-param标签配置信息-->
    <init-param>
        <!-- param-name标签配置KEY -->
        <param-name>username</param-name>
        <!-- param-value标签配置VALUE -->
        <param-value>url</param-value>
    </init-param>
    <init-param>
        <param-name>pwd</param-name>
        <param-value>123456</param-value>
    </init-param>
    
</servlet>

代码

public class Demo01FirstServlet implements Servlet {
    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        // 1、可以获取 Servlet 程序的别名 servlet-name 的值 
        String servletName = servletConfig.getServletName();
        System.out.println("Demo01FirstServlet别名:" + servletName);
        // 2、获取初始化参数 init-param 
        Enumeration<String> initParameterNames = servletConfig.getInitParameterNames();
        Iterator<String> stringIterator = initParameterNames.asIterator();
        while (stringIterator.hasNext()){
            System.out.println(stringIterator.next() + "==>" + servletConfig.getInitParameter(stringIterator.next()));
        }
        // 3、获取 ServletContext 对象
        ServletContext servletContext = servletConfig.getServletContext();
        System.out.println(servletContext);
    }
}

结果

Demo01FirstServlet别名:FirstServlet
pwd==>123456
username==>url
org.apache.catalina.core.ApplicationContextFacade@1581b8a8
Demo01FirstServlet 被访问了。

2.2 继承HttpServlet类获取Config对象

public class Demo02HttpServlet extends HttpServlet {

    @Override
    public void init(ServletConfig config) throws ServletException {
        
        super.init(config);//超类中维护了一个ServletConfig对象,不调用超类方法则需要自己保存
        
        // 1、可以获取 Servlet 程序的别名 servlet-name 的值
        String servletName = config.getServletName();
        System.out.println("Demo01FirstServlet别名:" + servletName);
        // 2、获取初始化参数 init-param
        //只能访问本Servlet的配置文件,不能访问其他Servlet的
        Enumeration<String> initParameterNames = config.getInitParameterNames();
        Iterator<String> stringIterator = initParameterNames.asIterator();
        while (stringIterator.hasNext()){
            String next = stringIterator.next();
            System.out.println(next + "==>" + config.getInitParameter(next));
        }
        // 3、获取 ServletContext 对象
        ServletContext servletContext = config.getServletContext();
        System.out.println(servletContext);
    }
}

3 ServletContext类

​ ServletContext对象表示Servlet应用程序。每个Web应用程序都只有一个ServletContext对象。在将一个应用程序同时部署到多个容器的分布式环境中,每台Java虚拟机上的Web应用都会有一个ServletContext对象。

​ 通过在ServletConfig中调用getServletContext方法,也可以获得ServletContext对象。

​ 有了ServletContext对象,就可以共享从应用程序中的所有资料处访问到的信息,并且可以动态注册Web对象。前者将对象保存在ServletContext中的一个内部Map中。保存在ServletContext中的对象被称作属性。相关方法如下:

Object getAttribute(String var1);
 
Enumeration<String> getAttributeNames();
 
void setAttribute(String var1, Object var2);
 
void removeAttribute(String var1);

3.1 ServletContext的特点

  1. ServletContext 是一个接口,它表示 Servlet 上下文对象
  2. 一个 web 工程,只有一个 ServletContext 对象实例。
  3. ServletContext 对象是一个域对象。
  4. ServletContext 是在 web 工程部署启动的时候创建。在 web 工程停止的时候销毁。

3.2 ServletContext的作用

  1. 获取 web.xml 中配置的上下文参数 context-param
  2. 获取当前的工程路径,格式: /工程路径
  3. 获取工程部署后在服务器硬盘上的绝对路径
  4. 像 Map 一样存取数据

配置文件

<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">

    <!-- context-param 用来配置全局文件,形式为键值对 -->
    <context-param>
        <param-name>key1</param-name>
        <param-value>value1</param-value>
    </context-param>
    <context-param>
        <param-name>key2</param-name>
        <param-value>value2</param-value>
    </context-param>
    
    <!-- 省略其它Servlet配置信息 -->
    
</web-app>

代码

public class ContextServlet1 extends HttpServlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取Context对象
        ServletContext context = getServletContext();
        // 1. 获取 web.xml 中配置的上下文参数 context-param
        Enumeration<String> names = context.getInitParameterNames();
        Iterator<String> iterator = names.asIterator();
        while (iterator.hasNext()) {
            String next = iterator.next();
            System.out.println(next + "==>" + context.getInitParameter(next));
        }
        // 2. 获取当前的工程路径,格式: /工程路径
        String contextPath = context.getContextPath();
        System.out.println("当前工程路径:" + contextPath);
        // 3. 获取工程部署后在服务器硬盘上的绝对路径
        String realPath = context.getRealPath("/");
        System.out.println("当前工程的绝对路径:" + realPath);
        // 4. 像 Map 一样存取数据
        System.out.println("保存之前: 获取 key1 的值是:"+ context.getAttribute("addKey"));//存储前无法获取数据
        context.setAttribute("addKey","addValue");
        System.out.println("保存之后: 获取 key1 的值是:"+ context.getAttribute("addKey"));//存储后可以获取数据
    }
}

代码

public class ContextServlet2 extends HttpServlet {

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //获取Context对象
        ServletContext context = getServletContext();
        //获取context中的数据
        System.out.println("ContextServlet2: 获取 key1 的值是:"+ context.getAttribute("addKey"));//可以获取其他Servlet存入的数据
    }
}

结果

key1==>value1
key2==>value2
当前工程路径:/01StudyServlet
当前工程的绝对路径:D:\0001TestNode\StudyJavaWeb\out\artifacts\01StudyServlet_war_exploded\
保存之前: 获取 key1 的值是:null
保存之后: 获取 key1 的值是:addValue
ContextServlet2: 获取 key1 的值是:addValue

4 HTTP协议

4.1 什么是HTTP协议

​ 网络程序开发,客户端和服务器实现数据交换,通信双方必须遵守通信协议.

HTTP协议,(HTTP,HyperText Transfer Protocol) 超文本传输协议,互联网程序开发基础,使用的版本1.1

  • HTTP/1.0,发送请求,创建一次连接,获得一个web资源,连接断开。
  • HTTP/1.1,发送请求,创建一次连接,获得多个web资源,连接断开。

HTTP的内容:规定了通信双方需要遵守的内容

HTTP的请求:客户端主动向服务器发送数据的请求

HTTP的响应:服务器将数据发回到客户端

4.2 客户端GET请求

  • 请求行
    • 提交方式
    • 请求服务器的地址和参数
    • 协议版本
  • 请求头
    • 键值对数据,客户端指导服务器信息
  • 无请求体

4.3 客户端POST请求

  • 请求行
    • 提交方式
    • 服务器地址
    • 协议版本
  • 请求头
    • 键值对数据,客户端指导服务器信息
  • 请求体
    • POST请求,提交的参数放在请求体中
    • 请求头和请求体之间有空行分割

4.4 服务器响应内容

  • 响应行
    • 协议版本
    • 状态码
      • 200 请求成功(找到了服务器上的资源)
      • 302 请求重定向
      • 304 请求资源没有改变,访问本地缓存
      • 404 请求的资源不存在,通常是用户路径编写错误,也可能是服务器资源已删除。
      • 500 服务器内部错误,通常程序抛异常
  • 响应头
    • 指导性信息,服务器指导浏览器,数据格式k:v
  • 响应体
    • 页面正文部分,浏览器要显示内容,和响应头有空行分割

4.5 数据类型:MIME

​ MIME 是 HTTP 协议中数据类型。

​ MIME 的英文全称是"Multipurpose Internet Mail Extensions" 多功能 Internet 邮件扩充服务。MIME 类型的格式是“大类型/小类型”,并与某一种文件的扩展名相对应。

常见的MIME类型:

文件原类型MIME 类型
超文本标记语言文本.html , .htmtext/html
普通文本.txttext/plain
RTF 文本.rtfapplication/rtf
GIF 图形.gifimage/gif
JPEG 图形.jpeg,.jpgimage/jpeg
au 声音文件.auaudio/basic
MIDI 音乐文件mid,.midiaudio/midi,audio/x-midi
RealAudio 音乐文件.ra, .ramaudio/x-pn-realaudio
MPEG 文件.mpg,.mpegvideo/mpeg
AVI 文件.avivideo/x-msvideo
GZIP 文件.gzapplication/x-gzip
TAR 文件.tarapplication/x-tar

5 HttpServletRequest类

5.1 ServletRequset接口

​ Servlet容器对于接受到的每一个Http请求,都会创建一个ServletRequest对象,并把这个对象传递给Servlet的Sevice( )方法。其中,ServletRequest对象内封装了关于这个请求的许多详细信息。其中的方法:

public interface ServletRequest {
  
 
    int getContentLength();//返回请求主体的字节数
 
    String getContentType();//返回主体的MIME类型
 
    String getParameter(String var1);//返回请求参数的值
 
}

其中,getParameter是在ServletRequest中最常用的方法,可用于获取查询字符串的值。

5.2 HttpServletRequest类的作用

​ 每次只要有请求进入 Tomcat 服务器,Tomcat 服务器就会把请求过来的 HTTP 协议信息解析好封装到 Request 对象中。然后传递到 service 方法(doGet 和 doPost)中给我们使用。我们可以通过 HttpServletRequest 对象,获取到所有请求的信息。

5.3 常用方法

	String getRequestURI(); 获取请求的资源路径 
    StringBuffer getRequestURL(); 获取请求的统一资源定位符(绝对路径) 
	String getRemoteHost() 获取客户端的 ip 地址 
	String getHeader(String var1); 获取请求头 
	String getParameter() 获取请求的参数 
	String[] getParameterValues() 获取请求的参数(多个值的时候使用) 
	String getMethod(); 获取请求的方式 GET 或 POST 
	boolean setAttribute(key, value); 设置域数据 
	String getAttribute(key); 获取域数据 
	Servlet getRequestDispatcher() 获取请求转发对象

代码

<!DOCTYPE html>
<html >
  <head>
    <meta charset="UTF-8">
    <title>form</title>
  </head>
  <body>
    <form action="http://localhost:8088/01StudyServlet/demo05Req" method="get">
      用户名:<input type="text" name="username"><br/>
      密码:<input type="password" name="password"><br/>
      兴趣爱好:<input type="checkbox" name="hobby" value="cpp">C++
                <input type="checkbox" name="hobby" value="java">Java
                <input type="checkbox" name="hobby" value="js">JavaScript<br/>
      <input type="submit" value="提交">
    </form>
  </body>
</html>

代码

public class Demo05Req extends HttpServlet {
    /*
        测试部分方法
        String getRequestURI(); 获取请求的资源路径
        StringBuffer getRequestURL(); 获取请求的统一资源定位符(绝对路径)
        String getRemoteHost() 获取客户端的 ip 地址
        String getHeader(String var1); 获取请求头
        String getMethod(); 获取请求的方式 GET 或 POST
        String getParameter() 获取请求的参数
        String[] getParameterValues() 获取请求的参数(多个值的时候使用)
     */
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //String getRequestURI(); 获取请求的资源路径
        String uri = req.getRequestURI();
        System.out.println("请求的资源路径:" + uri);
        
        //StringBuffer getRequestURL(); 获取请求的统一资源定位符(绝对路径)
        StringBuffer url = req.getRequestURL();
        System.out.println("统一资源定位符:" + url);
        
        //String getRemoteHost() 获取客户端的 ip 地址
        String id = req.getRequestedSessionId();
        System.out.println("ID:" + id);
        
        //String getHeader(String var1); 获取请求头信息 以key = User-Agent 为例
        String header_User_Agent = req.getHeader("User-Agent");
        System.out.println("获取请求头信息(User-Agent):" + header_User_Agent);
        
        //String getMethod(); 获取请求的方式 GET 或 POST
        String method = req.getMethod();
        System.out.println("请求的方式:" + method);
        
        //String getParameter() 获取请求的参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        System.out.println("用户名:" + username);
        System.out.println("密码:" + password);
        //String[] getParameterValues() 获取请求的参数(多个值的时候使用)
        String[] hobbys = req.getParameterValues("hobby");
        System.out.println("兴趣爱好:" + Arrays.toString(hobbys));
    }
}

结果

请求的资源路径:/01StudyServlet/demo05Req
统一资源定位符:http://localhost:8088/01StudyServlet/demo05Req
ID:null
获取请求头信息(User-Agent):Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:84.0) Gecko/20100101 Firefox/84.0
请求的方式:GET
用户名:nxy123
密码:123456
兴趣爱好:[cpp, java, js]

出现的问题:当使用POST请求方式且输入的用户名为中文时出现乱码问题,如下图所示,

在这里插入图片描述

解决问题:是编码解码方式不一致导致的,可以使用如下方法解决,

 protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置请求体的字符集为 UTF-8,从而解决 post 请求的中文乱码问题
        req.setCharacterEncoding("UTF-8");

        //String getMethod(); 获取请求的方式 GET 或 POST
        String method = req.getMethod();
        System.out.println("请求的方式:" + method);

        //String getParameter() 获取请求的参数
        String username = req.getParameter("username");
        String password = req.getParameter("password");
        System.out.println("用户名:" + username);
        System.out.println("密码:" + password);
        //String[] getParameterValues() 获取请求的参数(多个值的时候使用)
        String[] hobbys = req.getParameterValues("hobby");
        System.out.println("兴趣爱好:" + Arrays.toString(hobbys));
    }

在这里插入图片描述

如果GET请求中出现乱码,可以使用如下方式解决:

	// 获取请求参数 
	String username = req.getParameter("username"); 
	//1 先以 iso8859-1 进行编码 2 再以 utf-8 进行解码 
	username = new String(username.getBytes("iso-8859-1"), "UTF-8");

5.4 请求转发

请求转发图示

在这里插入图片描述

特点:

  1. 浏览器的地址不会发生变化;
  2. 实质上是一次请求;
  3. 转发前后的Servlet共享Request中的数据;
  4. 可以使用请求转发访问到WEB-INF保护目录下的资源;
  5. 不可以使用请求转发访问工程以外的资源。

代码

public class ForwardServlet01 extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1 获取请求参数
        String username = req.getParameter("username");
        //2 进行逻辑处理
        System.out.println("【1-逻辑处理】ForwardServlet01进行相关逻辑处理");
        //3 添加处理标识
        req.setAttribute("key1","【ForwardServlet01已经处理】");
        //4 创建请求转发对象并进行请求转发
        req.getRequestDispatcher("/forwardServlet02").forward(req,resp);
    }

public class ForwardServlet02 extends HttpServlet {
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //1 获取请求参数
        String username = req.getParameter("username");
        //2 查看上层处理结果
        System.out.println("【2-查看结果】" + req.getAttribute("key1"));
        //3 进行逻辑处理
        System.out.println("【2-逻辑处理】ForwardServlet02进行相关逻辑处理");
    }
}

结果

在这里插入图片描述

5.5 一些概念

5.5.1 base标签

<!DOCTYPE html> 
<html lang="zh_CN"> 
    <head> <meta charset="UTF-8"> 
        <title>Title</title>
        <!--base 标签设置页面相对路径工作时参照的地址 href 属性就是参数的地址值 --> 
        <base href="http://localhost:8080/07_servlet/a/b/">
    </head> 
    <body> 
        这是 a 下的 b 下的 c.html 页面<br/> 
        <a href="../../index.html">跳回首页</a><br/> 
    </body> 
</html>

5.5.2 JavaWeb中绝对路径和相对路径

相对路径:

  • . :表示当前目录

  • …: 表示上一级目录

  • 资源名:表示当前目录/资源名

绝对路径:

  • http://ip:port/工程路径/资源路径

注意:在实际开发中都使用绝对路径,使用相对路径时也会和base标签一起使用。

5.5.3 JavaWeb中‘/’的意义

在 web 中 / 斜杠 是一种绝对路径。

  • / 如果被浏览器解析,得到的地址是:http://ip:port/
  • / 如果被服务器解析,得到的地址是:http://ip:port/工程路径
  • 特殊情况:response.sendRediect(“/”);本质上是浏览器进行解析,所以为http://ip:port/

6 HttpServletResponse类

6.1 ServletResponse接口

​ javax.servlet.ServletResponse接口表示一个Servlet响应,在调用Servlet的Service()方法前,Servlet容器会先创建一个ServletResponse对象,并把它作为第二个参数传给Service()方法。ServletResponse隐藏了向浏览器发送响应的复杂过程。其中的方法:

public interface ServletResponse {
    String getCharacterEncoding();
 
    String getContentType();
 
    ServletOutputStream getOutputStream() throws IOException;
 
    PrintWriter getWriter() throws IOException;
 
    void setCharacterEncoding(String var1);
 
    void setContentLength(int var1);
 
    void setContentType(String var1);
 
    void setBufferSize(int var1);
 
    int getBufferSize();
 
    void flushBuffer() throws IOException;
 
    void resetBuffer();
 
    boolean isCommitted();
 
    void reset();
 
    void setLocale(Locale var1);
 
    Locale getLocale();
}

​ 其中的getWriter方法,它返回了一个可以向客户端发送文本的的Java.io.PrintWriter对象。默认情况下,PrintWriter对象使用ISO-8859-1编码(该编码在输入中文时会发生乱码)。

​ 在向客户端发送响应时,大多数都是使用该对象向客户端发送HTML。

​ 还有一个方法也可以用来向浏览器发送数据,它就是getOutputStream,从名字就可以看出这是一个二进制流对象,因此这个方法是用来发送二进制数据的。

​ 在发送任何HTML之前,应该先调用setContentType()方法,设置响应的内容类型,并将“text/html”作为一个参数传入,这是在告诉浏览器响应的内容类型为HTML,需要以HTML的方法解释响应内容而不是普通的文本,或者也可以加上“charset=UTF-8”改变响应的编码方式以防止发生中文乱码现象。

6.2 HttpServletResponse 类的作用

​ HttpServletResponse 类和 HttpServletRequest 类一样。每次请求进来,Tomcat 服务器都会创建一个 Response 对象传递给 Servlet 程序去使用。HttpServletRequest 表示请求过来的信息,HttpServletResponse 表示所有响应的信息,我们如果需要设置返回给客户端的信息,都可以通过 HttpServletResponse 对象来进行设置。

6.3 两个输出流的说明

  • 字节流:使用getOutputStream()方法获取,常用于下载(传递二进制数据)
  • 字符流:使用getWriter()方法获取,常用于回传字符串(常用)

注意:两个流同时只能使用一个。使用了字节流,就不能再使用字符流,反之亦然,否则就会报错。

作用:使用流往客户端回传数据。

public class DemoResponse extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        PrintWriter writer = resp.getWriter();
        writer.write("DemoResponse");
    }
}

6.4 响应时乱码问题的解决

解决响应中文乱码方案一(不推荐使用):

//设置服务器字符集为  UTF-8
resp.setCharacterEncoding("UTF-8"); 

//通过响应头,设置浏览器也使用UTF-8字符集*
resp.setHeader("Content-Type", "text/html;charset=UTF-8");

解决响应中文乱码方案二(推荐):

//它会同时设置服务器和客户端都使用 UTF-8 字符集,还设置了响应头
//此方法一定要在获取流对象之前调用才有效
resp.setContentType("text/html; charset=UTF-8"); 

6.5 请求重定向

​ 请求重定向,是指客户端给服务器发请求,然后服务器响应客户端一个新的URL。让客户端去新的URL访问。叫请求重定向(因为之前的地址可能已经被废弃)。

图解

在这里插入图片描述

重定向的两种方式

请求重定向的第一种方案: 
	// 设置响应状态码 302 ,表示重定向
	resp.setStatus(302); 
	// 设置响应头,说明 新的地址在哪里 
	resp.setHeader("Location", "http://localhost:8080"); 
请求重定向的第二种方案(推荐使用): 
	resp.sendRedirect("http://localhost:8080");

6.5.1 第一种方式

代码

public class Servlet01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet01被访问了.");
        //设置响应码
        resp.setStatus(302);
        //设置响应头
        resp.setHeader("Location","/01StudyServlet/servlet02");
    }
}
public class Servlet02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet02被访问了.");
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = resp.getWriter();
        writer.write("<h1>我是Servlet02的响应</h1>");
    }
}

结果

在这里插入图片描述

6.5.2 第二种方式

代码

public class Servlet01 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet01被访问了.");
        resp.sendRedirect("/01StudyServlet/servlet02");
    }
}
public class Servlet02 extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("Servlet02被访问了.");
        resp.setContentType("text/html;charset=UTF-8");
        PrintWriter writer = resp.getWriter();
        writer.write("<h1>我是Servlet02的响应</h1>");
    }
}

结果

在这里插入图片描述

7 Cookie

7.1 概念

​ Cookie 是服务器通知客户端保存键值对的一种技术。客户端有了 Cookie 后,每次请求都发送给服务器。每个 Cookie 的大小不能超过 4kb。

7.2 创建Cookie

方法

		Cookie cookie1 = new Cookie("key","value");
        resp.addCookie(cookie1);

代码

@WebServlet(urlPatterns = "/cookieServlet")
public class CookieServlet extends HttpServlet {

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

        String order = req.getParameter("order");
        switch (order) {
            case "createCookie":
                createCookie(req, resp);
                break;
            case "getCookie":
                getCookie(req, resp);
                break;
            default:
                System.out.println("找不到命令" + order);
                break;
        }

    }

    /**
     * 创建Cookie
     * @param req
     * @param resp
     */
    private void createCookie(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        Cookie cookie1 = new Cookie("key1","value1");
        resp.addCookie(cookie1);
        Cookie cookie2 = new Cookie("key2","value2");
        resp.addCookie(cookie2);
        Cookie cookie3 = new Cookie("key3","value3");
        resp.addCookie(cookie3);

        resp.getWriter().print("创建成功!");

    }

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

在这里插入图片描述

7.3 获取Cookie

方法

	Cookie[] cookies = req.getCookies();

代码

/**
     * 获取Cookie
     * @param req
     * @param resp
     */
    private void getCookie(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        Cookie[] cookies = req.getCookies();
        for (Cookie cookie : cookies) {
            resp.getWriter().print("【" + cookie.getName() + "=" + cookie.getValue() + "】<br>");
        }
    }

效果
在这里插入图片描述

7.4 修改Cookie

方法

方案一: 
	1、先创建一个要修改的同名(指的就是 key)的 Cookie 对象 
	2、在构造器,同时赋于新的 Cookie 值
	3、调用 response.addCookie( Cookie )
    
方案二: 
	1、先查找到需要修改的 Cookie 对象 
	2、调用 setValue()方法赋于新的 Cookie 值
	3、调用 response.addCookie()通知客户端保存修改

代码

 /**
     * 修改Cookie的值
     * @param req
     * @param resp
     */
    private void updateCookie(HttpServletRequest req, HttpServletResponse resp) {
        //方案一:
        //1、先创建一个要修改的同名(指的就是 key)的 Cookie 对象
        //2、在构造器,同时赋于新的 Cookie 值
        Cookie cookie1 = new Cookie("key1", "newValue");
        //3、调用 response.addCookie( Cookie )
        resp.addCookie(cookie1);

        //方案二:
        //1、先查找到需要修改的 Cookie 对象
        Cookie[] cookies = req.getCookies();
        for (Cookie cookie : cookies) {
            if ("key2".equals(cookie.getName())) {
                //2、调用 setValue()方法赋于新的 Cookie 值
                cookie.setValue("newValue");
                //3、调用 response.addCookie()通知客户端保存修改
                resp.addCookie(cookie);
            }
        }
    }

效果

在这里插入图片描述

7.5 Cookie的生命周期

修改方式:使用setMaxAge()方法进行修改,

  • 正数,表示在指定的秒数后过期
  • 负数,表示浏览器一关,Cookie 就会被删除(默认值是-1)
  • 零,表示马上删除 Cookie

代码

 /**
     * Cookie的声明周期
     * @param req
     * @param resp
     */
    private void lifeCookie(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        //使用setMaxAge()方法进行修改
        Cookie[] cookies = req.getCookies();
        for (Cookie cookie : cookies) {
            if ("key1".equals(cookie.getName())) {
                //正数,表示在指定的秒数后过期
                cookie.setMaxAge(60 * 60);//设置一个小时的Cookie
                resp.addCookie(cookie);
            }
            if ("key2".equals(cookie.getName())) {
                //负数,表示浏览器一关,Cookie 就会被删除(默认值是-1)
                cookie.setMaxAge(-1);//设置默认值的Cookie
                resp.addCookie(cookie);
            }
            if ("key3".equals(cookie.getName())) {
                //零,表示马上删除 Cookie
                cookie.setMaxAge(0);//设置马上删除的Cookie
                resp.addCookie(cookie);
            }
        }
        resp.getWriter().print("Cookie的声明周期已经设置");
    }

效果

在这里插入图片描述

7.6 Cookie的有效路径

​ Cookie 的 path 属性可以有效的对Cookie进行过滤,从而使哪些 Cookie 可以发送给服务器,哪些不发。path 属性是通过请求的地址来进行有效的过滤。

示例

对Cookie的path进行设置:
	CookieA path=/工程路径 
	CookieB path=/工程路径/abc 
请求地址如下: 
    http://ip:port/工程路径/a.html 
        CookieA 发送 
        CookieB 不发送 
    http://ip:port/工程路径/abc/a.html 
        CookieA 发送 
        CookieB 发送

8 Session

8.1 概念

  1. Session 就一个接口(HttpSession)。
  2. Session 就是会话,这种技术用来维护一个客户端和服务器之间关联。
  3. 每个客户端都有自己的一个 Session 会话。
  4. Session 会话中,我们经常用来保存用户登录之后的信息

8.2 Session的创建和获取

方法

创建和获取Session的 API 是一样的。 
request.getSession() 
    第一次调用是:创建 Session 会话 
    之后调用都是:获取前面创建好的 Session 会话对象。 
isNew(); 判断到底是不是刚创建出来的(新的) 
    	true 表示刚创建 
    	false 表示获取之前创建 
每个会话都有一个ID值,这个 ID 是唯一的。 
getId() 得到 Session 的会话 id 值。

代码

@WebServlet(urlPatterns = "/sessionServlet")
public class SessionServlet extends HttpServlet {

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");

        String order = req.getParameter("order");
        switch (order) {
            case "createSession":
                createSession(req, resp);
                break;
            case "addDateSession":
                addDateSession(req, resp);
                break;
            case "getDateSession":
                getDateSession(req, resp);
                break;
            default:
                System.out.println("找不到命令" + order);
                break;
        }
    }
       /**
     * 获取一个Session
     * @param req
     * @param resp
     */
    private void createSession(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        //获取Session
        HttpSession session = req.getSession();
        //判断Session是不是刚刚创建的
        boolean aNew = session.isNew();
        resp.getWriter().print("Session是不是先创建的:" + aNew + "<br>");
        //获取Session的ID
        String id = session.getId();
        resp.getWriter().print("Session的ID:" + id);
    }

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

效果

在这里插入图片描述

8.3 Session数据读取

代码

    /**
     * 获取Session的数据
     * @param req
     * @param resp
     */
    private void getDateSession(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        //获取Session
        HttpSession session = req.getSession();
        //获取数据
        Object session1 = session.getAttribute("Session1");
        //响应数据给客户端
        resp.getWriter().print("Session的数据为:" + session1);
    }

    /**
     * 给Session添加数据
     * @param req
     * @param resp
     */
    private void addDateSession(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        //获取Session
        HttpSession session = req.getSession();
        //添加数据
        session.setAttribute("Session1","Session1");
        //返回提示信息
        resp.getWriter().print("已经添加数据至服务器。");
    }

效果

在这里插入图片描述

8.4 Session的生命周期

设置方法

  1. 给所有Session设置失效时间

    Session 默认的超时时间长为 30 分钟。因为在 Tomcat 服务器的配置文件 
    web.xml中默认有以下的配置,它就表示配置了当前 Tomcat 服务器下所有的 Session 超时配置默认时长为:30 分钟:
    <session-config> 
        <session-timeout>30</session-timeout> 
    </session-config>
    
    你希望你的 web 工程,默认的 Session 的超时时长为其他时长。你可以在你自己的 web.xml 配置文件中做 以上相同的配置。就可以修改你的 web 工程所有 Seession 的默认超时时长。 
    <!--表示当前 web 工程。创建出来 的所有 Session 默认是 20 分钟 超时时长--> 
    <session-config> 
        <session-timeout>20</session-timeout> 
    </session-config>
    
  2. 给个别Session设置失效时间

    public int getMaxInactiveInterval()获取 Session 的超时时间 
    值为正数的时候,设定 Session 的超时时长(以秒为单位)。 
    负数表示永不超时(极少使用)
    
    public void invalidate() 让当前 Session 会话马上超时无效。
    

8.5 Session的底层实现

  1. 第一次获取Session时,会创建一个Session示例保存在服务器的内存中,并且在响应时候以Cookie技术响应给客户端一个Session的ID值。
  2. 客户端浏览器会维护这个Cookie,以便在服务器能够取得同一个Session对象。

9 Listener监听器

9.1 概念

  1. Listener 监听器它是 JavaWeb 的三大组件之一。JavaWeb 的三大组件分别是:Servlet 程序、Filter 过滤器、Listener 监听器。
  2. Listener 它是 JavaEE 的规范,就是接口
  3. 监听器的作用是,监听某种事物的变化。然后通过回调函数,反馈给客户(程序)去做一些相应的处理。

9.2 ServletContextListener 监听器

​ ServletContextListener 它可以监听 ServletContext 对象的创建和销毁。ServletContext 对象在 web 工程启动的时候创建,在 web 工程停止的时候销毁。监听到创建和销毁之后都会分别调用 ServletContextListener 监听器的方法反馈。

接口中的两个抽象方法

public interface ServletContextListener extends EventListener { 
    /**
    在 ServletContext 对象创建之后马上调用,做初始化 
    */ 
    public void contextInitialized(ServletContextEvent sce); 
    /**
    在 ServletContext 对象销毁之后调用 
    */ 
    public void contextDestroyed(ServletContextEvent sce); 
}

实现类

public class MyServletContextListenerImpl implements ServletContextListener { 			@Override 
    public void contextInitialized(ServletContextEvent sce) { 					    		System.out.println("ServletContext 对象被创建了"); 
    }
    @Override 
    public void contextDestroyed(ServletContextEvent sce) {        							System.out.println("ServletContext 对象被销毁了"); 
    }

web.xml配置

<!--配置监听器--> 
<listener> 
    <listener-class>com.atguigu.listener.MyServletContextListenerImpl</listener-class> </listener>

10 Filter过滤器

10.1 概念

  1. Filter 过滤器它是 JavaWeb 的三大组件之一。三大组件分别是:Servlet 程序、Listener 监听器、Filter 过滤器。
  2. Filter 过滤器它是 JavaEE 的规范,也就是接口 。
  3. Filter 过滤器它的作用是:拦截请求,过滤响应。

常见的应用场景有:权限检查、日记操作、事务管理等。

10.2 创建Filter——以登录拦截为例

登录页面

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录页面</title>
</head>
<body>
    <form action="http://localhost:8088/01StudyServlet/loginServlet" method="get">
        用户名:<input type="text" name="username"> <br>
        密  码:<input type="password" name="password"> <br>
        <input type="submit"/>
    </form>
</body>
</html>

主页

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>主页</title>
</head>
<body>
    这个是主页。
</body>
</html>

处理登录请求的Servlet

@WebServlet(urlPatterns = "/loginServlet")
public class LoginServlet extends HttpServlet {

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        resp.setContentType("text/html;charset=utf-8");
        //创建变量存储用户名和密码
        String username = null;
        String password = null;
        //获取Session
        HttpSession session = req.getSession();

        //Session中没有信息,处理登录请求
        username = req.getParameter("username");
        password = req.getParameter("password");
        //判断请求
        if ("user".equals(username) && "1234".equals(password)) {
            //登录成功,添加用户信息
            session.setAttribute("user",username);
            //存储用户名和密码至Session中
            session.setAttribute("username",username);
            session.setAttribute("password",password);
            //进行页面跳转,index
            resp.getWriter().print("3秒后跳转到主页");
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resp.sendRedirect("/01StudyServlet/damin/index.html");
        }else{
            req.getRequestDispatcher("/01StudyServlet/login.html").forward(req,resp);
        }

    }

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

过滤器Filter

public class LoginFilter implements Filter {

    public void destroy() {
    }

    public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest httpServletRequest = (HttpServletRequest) req;
        HttpSession session = httpServletRequest.getSession();
        Object user = session.getAttribute("user");
        if (user != null){  //登录成功
            chain.doFilter(req,resp);
        }else {
            httpServletRequest.getRequestDispatcher("/01StudyServlet/login.html").forward(req,resp);
        }
    }

    public void init(FilterConfig config) throws ServletException {

    }
}

Filter的配置文件(在web.xml中)

<!--filter 标签用于配置一个 Filter 过滤器-->
<filter><!--给 filter 起一个别名-->    
    <filter-name>loginFilter</filter-name>
    <!--配置 filter 的全类名-->
    <filter-class>demo11login.LoginFilter</filter-class>
</filter>
<!--filter-mapping 配置 Filter 过滤器的拦截路径-->
<filter-mapping>
    <!--filter-name 表示当前的拦截路径给哪个 filter 使用-->
    <filter-name>loginFilter</filter-name>
    <!--url-pattern 配置拦截路径 / 表示请求地址为:http://ip:port/工程路径/ 映射到 IDEA 的 web 目录 
	/admin/* 表示请求地址为:http://ip:port/工程路径/admin/* -->
    <url-pattern>/damin/*</url-pattern>
</filter-mapping>

10.3 Filter生命周期

Filter 的生命周期包含几个方法:

  • 构造器方法
  • init 初始化方法:第 1,2 步,在 web 工程启动的时候执行(Filter 已经创建) 。
  • doFilter 过滤方法,每次拦截到请求,就会执行 。
  • destroy 销毁,停止 web 工程的时候,就会执行(停止 web 工程,也会销毁 Filter 过滤器)。

10.4 FilterConfig 类

​ FilterConfig 类是 Filter 过滤器的配置文件类。Tomcat 每次创建 Filter 的时候,也会同时创建一个 FilterConfig 类,这里包含了 Filter 配置文件的配置信息。

作用:获取 filter 过滤器的配置内容,

  1. 获取 Filter 的名称 filter-name 的内容
  2. 获取在 Filter 中配置的 init-param 初始化参数
  3. 获取 ServletContext 对象

方法

获取 Filter 的名称 filter-name 的内容
	getFilterName()
获取在 Filter 中配置的 init-param 初始化参数  
	getInitParameter("username")
获取 ServletContext 对象
	getServletContext()

配置文件

<filter>
        <filter-name>loginFilter</filter-name>
        <filter-class>demo11login.LoginFilter</filter-class>
        <init-param>
            <param-name>username</param-name>
            <param-value>root</param-value>
        </init-param>
        <init-param>
            <param-name>password</param-name>
            <param-value>123456</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>loginFilter</filter-name>
        <url-pattern>/damin/*</url-pattern>
    </filter-mapping>

代码

public class LoginFilter implements Filter {

    //省略其他方法
    
    public void init(FilterConfig config) throws ServletException {
        //获取 Filter 的名称 filter-name 的内容 getFilterName()
        String filterName = config.getFilterName();
        System.out.println("filterName:" + filterName);
        //获取在 Filter 中配置的 init-param 初始化参数 getInitParameter("username")
        System.out.println("初始化参数 username 的值是:" + config.getInitParameter("username"));
        System.out.println("初始化参数 password 的值是:" + config.getInitParameter("password"));
        //获取 ServletContext 对象 getServletContext()
        System.out.println(config.getServletContext());
    }
}

效果

在这里插入图片描述

10.5 FilterChain过滤器链

chain.doFilter(request, response)的作用:

  1. 如果有下一个Filter,则执行下一个Filter。
  2. 如果没有下一个Filter,则访问目标资源。

多个Filter执行的特点:

  1. 所有的Filter都在同一个线程中执行。
  2. 所有的Filter共用一个request对象。
  3. 执行的顺序与在配置文件中的配置顺序相同。

配置文件

 	<filter>
        <filter-name>Filter1</filter-name>
        <filter-class>demo12Filter.Filter1</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>Filter1</filter-name>
        <url-pattern>/test.jsp</url-pattern>
    </filter-mapping>

    <filter>
        <filter-name>Filter2</filter-name>
        <filter-class>demo12Filter.Filter2</filter-class>
    </filter>
    <filter-mapping>
        <filter-name>Filter2</filter-name>
        <url-pattern>/test.jsp</url-pattern>
    </filter-mapping>

filter1

public class Filter1 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("Filter1前置代码。");
        chain.doFilter(request, response);
        System.out.println("Filter1后置代码。");
    }

    @Override
    public void destroy() {

    }
}

filter2

public class Filter2 implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("Filter2前置代码。");
        chain.doFilter(request, response);
        System.out.println("Filter2后置代码。");
    }

    @Override
    public void destroy() {

    }
}

jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>测试JSP</title>
</head>
<body>
    <%
        System.out.println("测试JSP代码");
    %>
</body>
</html>

效果

在这里插入图片描述

10.6 Filter的拦截路径

精确匹配

<url-pattern>/target.jsp</url-pattern> 
以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/target.jsp

目录匹配

<url-pattern>/admin/*</url-pattern> 
以上配置的路径,表示请求地址必须为:http://ip:port/工程路径/admin/*

后缀名匹配

<url-pattern>*.html</url-pattern> 
以上配置的路径,表示请求地址必须以.html 结尾才会拦截到 
<url-pattern>*.do</url-pattern> 
以上配置的路径,表示请求地址必须以.do 结尾才会拦截到 
<url-pattern>*.action</url-pattern> 
以上配置的路径,表示请求地址必须以.action 结尾才会拦截到
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值