Severlet、Cookie、Session 和 Filter 详解

一、Servlet 简介

Servlet(Server Applet),全称 Java Servlet,未有中文译文。

Servlet 是用 Java 编写的服务器端程序,其主要功能在于处理请求和发送响应,交互式地浏览和修改数据,生成动态 Web 内容
Servlet运行于支持Java的应用服务器中。从实现上讲,Servlet可以响应任何类型的请求,但绝大多数情况下Servlet只用来扩展基于HTTP协议的Web服务器。

二、Servlet 的工作原理

Servlet 接口定义了 Servlet 与 servlet 容器之间的契约。这个契约是:Servlet 容器将 Servlet 类载入内存,并产生 Servlet 实例和调用它具体的方法。但是要注意的是,在一个应用程序中,每种 Servlet 类型只能有一个实例

● 用户请求致使 Servlet 容器调用 Servlet 的 Service() 方法,并传入一个 ServletRequest 对象和一个 ServletResponse 对象。ServletRequest 对象和 ServletResponse 对象都是由 Servlet 容器(例如TomCat)封装好的,并不需要程序员去实现,程序员可以直接使用这两个对象
● ServletRequest 中封装了当前的 Http 请求,因此,开发人员不必解析和操作原始的 Http 数据。ServletResponse 表示当前用户的 Http 响应,程序员只需直接操作 ServletResponse 对象就能把响应轻松的发回给用户。

对于每一个应用程序,Servlet 容器还会创建一个 ServletContext 对象,这个对象中封装了上下文(应用程序)的环境详情。每个应用程序只有一个 ServletContext。每个 Servlet 对象也都有一个封装 Servlet 配置的 ServletConfig 对象

三、Servlet 的工作流程

在这里插入图片描述
Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:

① Web 服务器首先检查是否已经装载并创建了该 Servlet 的实例对象。如果是,则直接执行第 ④ 步,否则,执行第 ② 步。
② 装载并创建该 Servlet 的一个实例对象。
在这里插入图片描述
③ 调用 Servlet 实例对象的 init() 方法。
创建一个用于封装 HTTP 请求消息的 HttpServletRequest 对象和一个代表 HTTP 响应消息的 HttpServletResponse 对象
在这里插入图片描述
调用 Servlet 的 service() 方法并将请求和响应对象作为参数传递进去
在这里插入图片描述
⑥ 读取 Request 对象中的请求信息,并将相应信息写入 Response 对象中。
在这里插入图片描述
⑦ web 容器从Response 对象中读取内容组装成 HTTP 响应。
在这里插入图片描述
⑧ WEB 应用程序被停止或重新启动之前,Servlet 引擎将卸载 Servlet,并在卸载之前调用 Servlet 的 destroy() 方法。

Servlet 调用图
在这里插入图片描述

四、Tomcat 与Servlet

在这里插入图片描述

Tomcat 是 Web 应用服务器,是一个 Servlet/JSP 容器。Tomcat 作为 Servlet 容器,负责处理客户请求,把请求传送给 Servlet,并将 Servlet 的响应传送回给客户。而 Servlet 是一种运行在支持 Java 语言的服务器上的组件。Servlet 最常见的用途是扩展 Java Web 服务器功能,提供非常安全的,可移植的,易于使用的CGI替代品。
在这里插入图片描述
① Tomcat 将 http 请求文本接收并解析,然后封装成 HttpServletRequest 类型的 request 对象,所有的 HTTP 头数据读可以通过 request 对象调用对应的方法查询到。
② Tomcat 同时会要响应的信息封装为 HttpServletResponse 类型的 response 对象,通过设置 response 属性就可以控制要输出到浏览器的内容,然后将 response 交给 tomcat,tomcat 就会将其变成响应文本的格式发送给浏览器。

Java Servlet API 是 Servlet 容器(tomcat)和servlet之间的接口,它定义了 serlvet 的各种方法,还定义了 Servlet 容器传送给 Servlet 的对象类,其中最重要的就是 ServletRequest 和 ServletResponse。所以说我们在编写 servlet 时,需要实现 Servlet 接口,按照其规范进行操作。

五、Servlet 生命周期

Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:

① Servlet 初始化后调用 init () 方法。
② Servlet 调用 service() 方法来处理客户端的请求。
③ Servlet 销毁前调用 destroy() 方法。
④ 最后 Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。

1、init() 方法

init 方法只能被调用一次。它在第一次创建 Servlet 时被调用,在后续每次用户请求时不再调用。因此,它是用于一次性初始化,就像 Applet 的 init 方法一样。

Servlet 创建于用户第一次调用对应于该 Servlet 的 URL 时,但是您也可以指定 Servlet 在服务器第一次启动时被加载

● 当用户调用一个 Servlet 时,就会创建一个 Servlet 实例,每一个用户请求都会产生一个新的线程,适当的时候移交给 doGet 或 doPost 方法。init() 方法简单地创建或加载一些数据,这些数据将被用于 Servlet 的整个生命周期

init 方法的定义如下:

public void init() throws ServletException {
  // 初始化代码...
}

2、service() 方法

● service() 方法是执行实际任务的主要方法。Servlet 容器(即 Web 服务器)调用 service() 方法来处理来自客户端(浏览器)的请求,并把格式化的响应写回给客户端

每次服务器接收到一个 Servlet 请求时,服务器会产生一个新的线程并调用服务。service() 方法检查 HTTP 请求类型(GET、POST、PUT、DELETE 等),并在适当的时候调用 doGet、doPost、doPut,doDelete 等方法。

public void service(ServletRequest request, ServletResponse response) 
				throws ServletException, IOException{ 
}

service() 方法由容器调用,service 方法在适当的时候调用 doGet、doPost、doPut、doDelete 等方法。所以,您不用对 service() 方法做任何动作,您只需要根据来自客户端的请求类型来重写 doGet() 或 doPost() 即可

3、doGet() 方法

GET 请求来自于一个 URL 的正常请求,或者来自于一个未指定 method 的 HTML 表单,它由 doGet() 方法处理。

public void doGet(HttpServletRequest request,HttpServletResponse response)
    throws ServletException, IOException {
    // Servlet 代码
}

4、doPost() 方法

POST 请求来自于一个特别指定了 method 为 POST 的 HTML 表单,它由 doPost() 方法处理。

public void doPost(HttpServletRequest request, HttpServletResponse response)
    throws ServletException, IOException {
    // Servlet 代码
}

5、destroy() 方法

destroy() 方法只会被调用一次,在 Servlet 生命周期结束时被调用。destroy() 方法可以让您的 Servlet 关闭数据库连接、停止后台线程、把 Cookie 列表或点击计数器写入到磁盘,并执行其他类似的清理活动

● 在调用 destroy() 方法之后,servlet 对象被标记为垃圾回收

destroy 方法定义如下所示:

  public void destroy() {
    // 终止化代码...
  }

1、典型的 Servlet 生命周期方案

● 第一个到达服务器的 HTTP 请求被委派到 Servlet 容器。
● Servlet 容器在调用 service() 方法之前加载 Servlet。
● 然后 Servlet 容器处理由多个线程产生的多个请求,每个线程执行一个单一的 Servlet 实例的 service() 方法。
在这里插入图片描述

2、ServletRequest 与 HttpServletRequest

(1)ServletRequest

ServletRequest 类表示来自客户端的请求。当 Servlet 容器接收到客户端要求访问特定 Servlet 的请求时,容器先解析客户端的原始请求数据,把它包装成一个 ServletRequest 对象。当容器调用 Servlet 对象的 service() 方法时,就可以把 ServletRequest 对象作为参数传给 service() 方法。

public interface ServletRequest {
	
	Object getAttribute(String var1);

    Enumeration <String> getAttributeNames();

    String getCharacterEncoding();

    void setCharacterEncoding(String var1) throws UnsupportedEncodingException;

    int getContentLength();

    long getContentLengthLong();

    String getContentType();

    ServletInputStream getInputStream() throws IOException;

    String getParameter(String var1);

    Enumeration <String> getParameterNames();

    String[] getParameterValues(String var1);

    Map<String, String[]> getParameterMap();

    String getProtocol();

    String getScheme();

    String getServerName();

    int getServerPort();

    BufferedReader getReader() throws IOException;

    String getRemoteAddr();

    String getRemoteHost();

    void setAttribute(String var1, Object var2);

    void removeAttribute(String var1);

    Locale getLocale();

    Enumeration<Locale> getLocales();

    boolean isSecure();

    RequestDispatcher getRequestDispatcher(String var1);

    /** @deprecated */
    String getRealPath(String var1);

    int getRemotePort();

    String getLocalName();

    String getLocalAddr();

    int getLocalPort();

    ServletContext getServletContext();

    AsyncContext startAsync() throws IllegalStateException;

    AsyncContext startAsync(ServletRequest var1, ServletResponse var2) throws IllegalStateException;

    boolean isAsyncStarted();

    boolean isAsyncSupported();

    AsyncContext getAsyncContext();

    DispatcherType getDispatcherType(); } 

(2)HttpServletRequest

HttpServletRequest 接口是 ServletRequest 接口的子接口。 HttpServletRequest 比
ServletRequest 多了一些针对于 Http 协议的方法。
HttpServlet类的重载service()方法及doGet()和doPost()等方法都有一个HttpServletRequest类型的参数。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3、HttpSercletResponse

HttpServletResponse对象作用:修改和设置响应信息。

(1)响应行

● 设置状态码

response.setStatus()  

● 设置状态码,调用默认对应页面

 response.sendError() 

(2)响应头

设置响应头

response.setHeader(“name”,”value”) 

(3)实体内容

● 发送字符实体内容

response.getWriter().writer();  

● 发送字节实体内容

response.getOutputStream().writer() ; 

六、Servlet 异常处理

当一个 Servlet 抛出一个异常时,Web 容器在使用了 exception-type 元素的 web.xml 中搜索与抛出异常类型相匹配的配置。
您必须在 web.xml 中使用 error-page 元素来指定对特定异常 或 HTTP 状态码 作出相应的 Servlet 调用。

假设,有一个 ErrorHandler 的 Servlet 在任何已定义的异常或错误出现时被调用。以下将是在 web.xml 中创建的项。

<!-- servlet 定义 -->
<servlet>
	<servlet-name>ErrorHandler</servlet-name>
	<servlet-class>ErrorHandler</servlet-class>
</servlet>
<!-- servlet 映射 -->
<servlet-mapping>
	<servlet-name>ErrorHandler</servlet-name>
	<url-pattern>/ErrorHandler</url-pattern>
</servlet-mapping>

<!-- error-code 相关的错误页面 -->
<error-page>
    <error-code>404</error-code>
    <location>/ErrorHandler</location>
</error-page>
<error-page>
    <error-code>403</error-code>
    <location>/ErrorHandler</location>
</error-page>

<!-- exception-type 相关的错误页面 -->
<error-page>
    <exception-type>
          javax.servlet.ServletException
    </exception-type >
    <location>/ErrorHandler</location>
</error-page>

<error-page>
    <exception-type>java.io.IOException</exception-type >
    <location>/ErrorHandler</location>
</error-page>

如果您想对所有的异常有一个通用的错误处理程序,那么应该定义下面的 error-page,而不是为每个异常定义单独的 error-page 元素:

<error-page>
    <exception-type>java.lang.Throwable</exception-type >
    <location>/ErrorHandler</location>
</error-page>

以下是关于上面的 web.xml 异常处理要注意的点:

● Servlet ErrorHandler 与其他的 Servlet 的定义方式一样,且在 web.xml 中进行配置。
● 如果有错误状态代码出现,不管为 404(Not Found 未找到)或 403(Forbidden 禁止),则会调用ErrorHandler 的 Servlet。
● 如果 Web 应用程序抛出 ServletException 或 IOException,那么 Web 容器会调用 ErrorHandler 的 Servlet。
● 您可以定义不同的错误处理程序来处理不同类型的错误或异常。

七、Servlet Cookie 处理

Cookie 是存储在客户端计算机上的文本文件,并保留了各种跟踪信息。Java Servlet 显然支持 HTTP Cookie。

1、为什么需要 Cookie

HTTP协议本身是无状态的,即服务器无法判断用户身份。Cookie实际上是一小段的文本信息(key-value格式)。客户端向服务器发起请求,如果服务器需要记录该用户状态,就使用 response 向客户端浏览器颁发一个 Cookie。客户端浏览器会把Cookie保存起来,当浏览器再请求该网站时,浏览器把请求的网址连同该 Cookie 一同提交给服务器。服务器检查该 Cookie,以此来辨认用户状态

打个比方,我们去银行办理储蓄业务,第一次给你办了张银行卡,里面存放了身份证、密码、手机等个人信息。当你下次再来这个银行时,银行机器能识别你的卡,从而能够直接办理业务。

2、cookie 机制

当用户第一次访问并登陆一个网站的时候,cookie的设置以及发送会经历以下步骤:

● 客户端发送一个请求到服务器,服务器发送一个 HttpResponse 响应到客户端,其中包含 Set-Cookie 的头部,也就是一组 Cookie,例如姓名、年龄或识别号码等。
浏览器将这些信息存储在本地计算机上,以备将来使用
当下一次浏览器向 Web 服务器发送任何请求时,HttpRequest 请求中会包含一个 Cookie 的头部,服务器将使用这些信息来识别用户
在这里插入图片描述

Servlet Cookie 处理需要对中文进行编码与解码,方法如下:

String str = java.net.URLEncoder.encode("中文""UTF-8");            //编码
String str = java.net.URLDecoder.decode("编码后的字符串","UTF-8");   // 解码

3、cookie属性项

在这里插入图片描述

4、Servlet Cookie 方法

在这里插入图片描述

5、修改或者删除Cookie

HttpServletResponse 提供的 Cookie 操作只有一个 addCookie(Cookie cookie),所以想要修改 Cookie 只能使用一个同名的 Cookie 来覆盖原先的 Cookie。如果要删除某个 Cookie,则只需要新建一个同名的 Cookie,并将 maxAge 设置为 0,并覆盖原来的 Cookie 即可。

新建的 Cookie,除了 value、maxAge 之外的属性,比如 name、path、domain 都必须与原来的一致才能达到修改或者删除的效果。否则,浏览器将视为两个不同的 Cookie 不予覆盖。

值得注意的是,从客户端读取 Cookie时,包括 maxAge 在内的其他属性都是不可读的,也不会被提交。浏览器提交 Cookie 时只会提交 name 和 value 属性,maxAge 属性只被浏览器用来判断 Cookie 是否过期,而不能用服务端来判断。

通过 Servlet 设置 Cookie

(1) 创建一个 Cookie 对象

您可以调用带有 cookie 名称和 cookie 值的 Cookie 构造函数,cookie 名称和 cookie 值都是字符串。
请记住,无论是名字还是值,都不应该包含空格或以下任何字符:[ ] ( ) = , " / ? @ : ;

 Cookie cookie = new Cookie("key","value"); 

(2) 设置最大生存周期

您可以使用 setMaxAge 方法来指定 cookie 能够保持有效的时间(以秒为单位)。下面将设置一个最长有效期为 24 小时的 cookie。

cookie.setMaxAge(60*60*24);  

(3) 发送 Cookie 到 HTTP 响应头

您可以使用 response.addCookie 来添加 HTTP 响应头中的 Cookie,如下所示:

response.addCookie(cookie); 

通过 Servlet 删除 Cookie
删除 Cookie 是非常简单的。如果您想删除一个 cookie,那么您只需要按照以下三个步骤进行:

● 读取一个现有的 cookie,并把它存储在 Cookie 对象中。
使用 setMaxAge() 方法设置 cookie 的年龄为零,来删除现有的 cookie
把这个 cookie 添加到响应头

6、通过 Servlet 读取 Cookie

要读取 Cookie,您需要通过调用 HttpServletRequest 的 getCookies( ) 方法创建一个 javax.servlet.http.Cookie 对象的数组。然后循环遍历数组,并使用 getName() 和 getValue() 方法来访问每个 cookie 和关联的值。

八、Servlet Session 跟踪

HTTP 是一种"无状态"协议,这意味着每次客户端检索网页时,客户端打开一个单独的连接到 Web 服务器,服务器会自动不保留之前客户端请求的任何记录。
为了保持浏览器与服务器之间的联系,才有了Session。Session 就是用于在服务器端保存用户状态的协议。通常用来保存用户的登录状态。
当访问一个页面的时候给浏览器创建一个独一无二的号码,也给同时创建的session赋予同样的号码。这样就可以在打开同一个网站的第二个页面时获取到第一个页面中session保留下来的对应信息(理解:当访问第二个页面时将号码同时传递到第二个页面。找到对应的session。)。这个号码也叫sessionID,session的ID号码,session的独一无二号码。

1、Session 工作原理

Session 内容保存在服务器端的,通常是保存在内存中,当然也可以保存在文件、数据库等等。客户端跟服务器端通过 Session ID 来关联,Session ID 通常以Cookie的形式存储在客户端。每次 HTTP 请求,Session ID 都会随着 Cookie 被传递到服务器端,这行就可以通过 Session ID取到对应的信息,来判断这个请求来自于哪个客户端/用户。

在这里插入图片描述
执行流程:

● 第一次请求,请求头中没有 jsessionid 的 cookie,当访问到对应的 servlet 资源时,执行到 getSession() 会创建 HttpSession 对象;进而响应时就将 session 的 id 作为 cookie 的 value,响应到浏览器 Set-cookie:jsessionid=xxxx;
● 再一次请求时,http 请求中就有一个 cookie:jsessionid=xxxx 信息,那么该 servlet 就可以通过 getSession() 获取到 jsessionid 在服务器内查找对应的 session 对象,有就使用,无就创建。

核心对象&职责
在这里插入图片描述

2、session 的两种实现方式

(1)cookies 实现

就是把 session ID 放在 cookie 里面(为什么是使用 cookies 存放呢,因为 cookie 有临时的,也有定时的,临时的就是当前浏览器什么时候关掉即消失,也就是说 session 本来就是当浏览器关闭即消失的,所以可以用临时的 cookie 存放。保存再 cookie里的 sessionID 一定不会重复,因为是独一无二的。),当允许浏览器使用 cookie 的时候,session 就会依赖于 cookies,当浏览器不支持 cookie 后,就可以通过第二种方式获取 session 内存中的数据资源。
这可能不是一种有效的方式,因为很多时候浏览器并不一定支持cookie,所以我们不建议使用这种方法来维持会话。

(2)URL重写

您可以在每个 URL 后面添加一些额外的数据来区分会话,服务器能够根据这些数据来关联 session 标识符
举例来说,http://w3cschool.cc/file.htm;sessionid=12345, session标识符为sessionid=12345,服务器可以用这个数据来识别客户端。
相比而言,重写URL是更好的方式来,就算浏览器不支持cookies也能工作,但缺点是您必须为每个URL动态指定session ID,就算这是个简单的HTML页面。

注意:

● 如果浏览器支持 cookie,创建 session 多大的时候,会被 session ID 保存在 cookie 里;只要允许 cookie,session 就不会改变,如果不允许使用 cookie,每刷新一次浏览器就会换一个 session(因为浏览器以为这是一个新的链接)。
如果不支持cookie,必须自己编程使用 URL 重写的方式实现 session
● Session 不像 cookie 一样拥有路径访问的问题,同一个 application 下的 servlet/jsp 都可以共享同一个 session,前提下是同一个客户端窗口。

3、使用建议/经验

(1)建议&经验

● Session 中保存的数据的大小要考虑到存储上限,不论是内存还是数据库
● Session 中不要存储不可恢复的内容
● 依赖 Session 的关键业务一定要确保客户端开启了Cookie 注意 Session 的过期时间
● 在负载均衡的情况下,由于存在 Web 服务器内存中的 Session 无法共享,通常需要重写 Session 的实现。

(2)常见的 Session 丢失的问题

Session 内容的丢失都是有原因的,通常都是由于 Web 服务器的重启造成的,比如 IIS、Tomcat 的重启

4、Session 创建、获取、销毁

// 获取session对象,服务器底层创建Session
HttpSession session = request.getSession();
// 获取session对象的唯一标识:sessionID (JSESSIONID=E925DE1EF00F7944537C01A3BC0E2688)
String jsessionid = session.getId();
// 销毁session对象中的jsessionid
session.invalidate();

5、Session 共享范围

http域对象之一,服务器中可跨资源共享数据。

// 往 session 中存储 msg
HttpSession session = request.getSession();
session.setAttribute("msg", "helloSession");
// 获取 msg
HttpSession session = request.getSession();
Object msg = session.getAttribute("msg");
// 删除域对象中的数据
session.removeAttribute("msg");

6、Session 生命周期

一般都是默认值 30 分钟,无需更改。
取决于 Tomcat 中 web.xml 默认配置:

<session-config>
    <session-timeout>30</session-timeout>
</session-config>

Session生命周期结束时机:

● 浏览器关闭:销毁Cookie中的jsessionid=xxx,原session对象会保留默认30min后才销毁,30分钟后为新的session;
● session销毁:主动调用 session.invalidate() 方法后,立即将session对象销毁,再次访问时会创建新的session。

7、session 对象的一些重要方法

在这里插入图片描述

九、监听器

十、过滤器 Filter

过滤器是处于客户端与服务器资源文件之间的一道过滤网,在访问资源文件之前,通过一系列的过滤器对请求进行修改、判断等,把不符合规则的请求在中途拦截或修改。也可以对响应进行过滤,拦截或修改响应。
Filter 也称之为过滤器,它是 Servlet 技术中最实用的技术,Web 开发人员通过 Filter 技术,对 web 服务器管理的所有web资源:例如 Jsp, Servlet, 静态图片文件或静态 html 文件等进行拦截,从而实现一些特殊的功能。例如实现 URL 级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能。

过滤器实际上就是对 web 资源进行拦截,做一些处理后再交给下一个过滤器或 servlet 处理,通常都是用来拦截 request 进行处理的,也可以对返回的 response 进行拦截处理
在这里插入图片描述
如图,浏览器发出的请求先递交给第一个 filter 进行过滤,符合规则则放行,递交给 filter 链中的下一个过滤器进行过滤。过滤器在链中的顺序与它在 web.xml 中配置的顺序有关,配置在前的则位于链的前端。当请求通过了链中所有过滤器后就可以访问资源文件了,如果不能通过,则可能在中间某个过滤器中被处理掉。

1、功能原理

Filter 接口中有一个 doFilter 方法,当我们编写好 Filter,并配置对哪个 web 资源进行拦截后,WEB 服务器每次在调用 web 资源的 service 方法之前,都会先调用一下 filter 的 doFilter 方法,因此,在该方法内编写代码可达到如下目的:

● 调用目标资源之前,让一段代码执行。
● 是否调用目标资源(即是否让用户访问web资源)。
● 调用目标资源之后,让一段代码执行。

web 服务器在调用 doFilter 方法时,会传递一个 filterChain 对象进来,filterChain 对象是 filter 接口中最重要的一个对象,它也提供了一个doFilter方法,开发人员可以根据需求决定是否调用此方法,调用该方法,则 web 服务器就会调用 web 资源的 service 方法,即 web 资源就会被访问,否则 web 资源不会被访问。
在 HttpServletRequest 到达 Servlet 之前,拦截客户的 HttpServletRequest ,根据需要检查 HttpServletRequest,也可以修改 HttpServletRequest 头和数据
在 HttpServletResponse 到达客户端之前,拦截 HttpServletResponse ,根据需要检查 HttpServletResponse,也可以修改 HttpServletResponse 头和数据

2、Filter 应用场景

● 自动登录
● 统一设置编码格式
● 访问权限控制
● 敏感字符过滤等

举个例子:当没用使用过滤器的时候,用户在客户端填写了用户名和密码点击了登录按钮之后,客户端会发送请求直接给系统后端,后端再调用SQL语句来判断用户名和密码是否有问题。用户名和密码都没有问题,则让他登录系统主页。这是一个正常情况。然而现实生活中并不全是正常的情况,偶尔也会出现一些意外,一些聪明的程序员可以绕过你的登录界面,直接访问你的系统主页。而我们的Filter过滤器就是为了应对这样的情况的。
当我们使用了Filter过滤器的时候,同样的场景,用户每一次发送请求的时候都要经过Filter过滤器,那么我们可以在过滤器里面编辑逻辑代码,进行判断当用户没有登录的时候,我们就让他跳转到提示错误的页面,在提示错误的页面里提示他只有登录了之后才可以访问系统主页!

3、Filter 的生命周期

与开发 Servlet 不同的是,Filter 接口并没有相应的实现类可供继承,要开发过滤器,只能直接实现 Filer 接口

(1) 加载和实例化

Web 容器启动时,即会根据 web.xml 中声明的 filter 顺序依次实例化这些 filter。 web.xml 中声明的每个 filter 在每个虚拟机中仅仅只有一个实例

(2) 初始化

public void init(FilterConfig filterConfig) throws ServletException; 

Web 容器调用 init(FilterConfig) 来初始化过滤器。容器在调用该方法时,向过滤器传递 FilterConfig 对象,FilterConfig 的用法和 ServletConfig 类似。利用 FilterConfig 对象可以得到 ServletContext 对象,以及在 web.xml 中配置的过滤器的初始化参数。在这个方法中,可以抛出 ServletException 异常,通知容器该过滤器不能正常工作。此时的 Web 容器启动失败,整个应用程序不能够被访问。实例化和初始化的操作只会在容器启动时执行,而且只会执行一次

FilterConfig 接口

用户在配置filter时,可以使用 <init-param> 为 filte r配置一些初始化参数,当 web 容器实例化 Filter 对象,调用其 init 方法时,会把封装了 filter 初始化参数的 filterConfig 对象传递进来。因此开发人员在编写 filter 时,通过 filterConfig 对象的方法,就可获得:

● String getFilterName():得到filter的名称。 String getInitParameter(String name): 返回在部署描述中指定名称的初始化参数的值。如果不存在返回null.
● Enumeration getInitParameterNames():返回过滤器的所有初始化参数的名字的枚举集合。
● public ServletContext getServletContext():返回Servlet上下文对象的引用。

(3) doFilter

public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException; 

doFilter 方法类似于 Servlet 接口的 service 方法。当客户端请求目标资源的时候,容器会筛选出符合 filter-mapping 中的 url-pattern 的 filter,并按照声明 filter-mapping 的顺序依次调用这些 filter 的 doFilter 方法。在这个链式调用过程中,可以调用 chain.doFilter(ServletRequest, ServletResponse) 将请求传给下一个过滤器(或目标资源),也可以直接向客户端返回响应信息,或者利用 RequestDispatcher 的 forward 和 include 方法,以及 HttpServletResponse 的 sendRedirect 方法将请求转向到其它资源。
需要注意的是,这个方法的请求和响应参数的类型是 ServletRequest 和 ServletResponse,也就是说,过滤器的使用并不依赖于具体的协议

chain.doFilter() 作用

chain.doFilter() 就是放行的作用
过滤器的作用就是之一就是在用户的请求到达servlet之前,拦截下来做预处理,处理之后便执行 chain.doFilter(request, response) 这个方法,如果还有别的过滤器,那么将处理好的请求传给下个过滤器,依此类推,当所有的过滤器都把这个请求处理好了之后,再将处理完的请求发给 servlet;如果就这一个过滤器,那么就将处理好的请求直接发给 servlet。

(4) 销毁

public void destroy(); 

Web 容器调用 destroy 方法指示过滤器的生命周期结束。在这个方法中,可以释放过滤器使用的资源。

4、Filter 链

在一个web应用中,可以开发编写多个Filter,这些Filter组合起来称之为一个Filter链。
web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter,当第一个Filter的doFilter方法被调用时,web服务器会创建一个代表Filter链的FilterChain对象传递给该方法。在doFilter方法中,开发人员如果调用了FilterChain对象的doFilter方法,则web服务器会检查FilterChain对象中是否还有filter,如果有,则调用第2个filter,如果没有,则调用目标资源。

5、Filter 的配置

(1)web.xml 方式配置

在web.xml中,监听器 > 过滤器 > servlet。也就是说 web.xml 中监听器配置在过滤器之前,过滤器配置在servlet之前,否则会出错

<?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">
     
    <!--指定过滤器的信息-->   
    <filter>
        <filter-name>AFilter</filter-name>
        <filter-class>filter.AFilter</filter-class>
    </filter>
    <filter>
        <filter-name>BFilter</filter-name>
        <filter-class>filter.BFilter</filter-class>
    </filter>

    <!--指定过滤器顺序以及过滤规则-->
    <filter-mapping>
        <filter-name>BFilter</filter-name>
        <url-pattern>/filter.jsp</url-pattern>
    </filter-mapping>

    <filter-mapping>
        <filter-name>AFilter</filter-name>
        <url-pattern>/filter.jsp</url-pattern>
    </filter-mapping>

</web-app>

(1)通过 <filter>标签指明过滤器信息:

● <description>:用于描述该标签,非必须;
● <filter-name>:为过滤器指定名称,必须的
● <filter-class>:指定该过滤器使用的 web 工程中的哪一个 filter 类,包含包名与类名,必须的;
● <init-param>:为过滤器的初始化提供参数。

(2)通过 <filter-mapping>标签进行请求与过滤器映射:

● <filter-name> 设置要映射过滤器的名称,该名称必须同 <filter> 标签下的 <filter-name> 的值一致
● <url-pattern> 设置过滤器要拦截过滤的请求路径,例如“/*”则表示对该 web 应用下所有的请求都进行拦截过滤。
● <servlet-name> 如果只要拦截过滤访问某个 Servlet,就可以使用该标签来替代 <url-pattern>。
● <dispatcher> 设置拦截过滤客户端请求的方式,有REQUEST,INCLUDE,FORWARD,ERROR四种(请注意均为大写)。非必须则默认为REQUEST,使用多个 <dispatcher>标签来设置多种请求方式。

注意:过滤器链的执行顺序跟 \<filter> 的顺序无关, \<filter-mapping> 的顺序才决定执行顺序
(3)<url-pattren> 的过滤规则:

● 过滤所有web资源:<url—pattern>/*</url-pattern> 则客户端请求访问任意资源文件时都要经过过滤器过滤,通过则访问文件,否则拦截。
● 过滤某一文件夹下所有文件:<url—pattern>/dir/*</url-pattern>
● 过滤某一种类型的文件:<url—pattern>*.扩展名</url-pattern>。比如<url—pattern>*.jsp</url-pattern>过滤所有对 jsp 文件的访问请求。
● 过滤某一文件夹下某一类型文件:<url—pattern>/dir/*.扩展名</url-pattern>

如果一个过滤器需要过滤多种文件,则可以配置多个 \<filter-mapping>,一个 mapping 定义一个url-pattern 来定义过滤规则

(2)@WebFilter 注解方式配置

@WebFilter 用于将一个类声明为过滤器,被 @WebFilter 注解的类会在容器启动时被加载,并进行属性配置,即项目一启动容器自动加载init方法,容器将根据具体的属性配置将相应的类部署为过滤器。
在这里插入图片描述
该注解具有下表给出的一些常用属性 ( 以下所有属性均为可选属性,但是 value、urlPatterns、servletNames 三者必需至少包含一个,且 value 和 urlPatterns 不能共存,如果同时指定,通常忽略 value 的取值 )
在这里插入图片描述

示例:

package com.xc.common.filter;

import java.io.IOException;
import java.util.Enumeration;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.annotation.WebInitParam;

@WebFilter(filterName = "FilterDemo02", urlPatterns = { "/*" }, initParams = { @WebInitParam(name = "name", value = "xc"),
		@WebInitParam(name = "like", value = "java") })
public class FilterDemo02 implements Filter {

	/*
	 * 过滤器初始化
	 * 
	 * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
	 */
	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		System.out.println("----FilterDemo02过滤器初始化----");

		// <filter>
		// <filter-name>FilterDemo02</filter-name>
		// <filter-class>me.gacl.web.filter.FilterDemo02</filter-class>
		// <!--配置FilterDemo02过滤器的初始化参数-->
		// <init-param>
		// <description>配置FilterDemo02过滤器的初始化参数</description>
		// <param-name>name</param-name>
		// <param-value>gacl</param-value>
		// </init-param>
		// <init-param>
		// <description>配置FilterDemo02过滤器的初始化参数</description>
		// <param-name>like</param-name>
		// <param-value>java</param-value>
		// </init-param>
		// </filter>
		//
		// <filter-mapping>
		// <filter-name>FilterDemo02</filter-name>
		// <!--“/*”表示拦截所有的请求 -->
		// <url-pattern>/*</url-pattern>
		// </filter-mapping>

		// 得到过滤器的名字
		String filterName = filterConfig.getFilterName();
		// 得到在web.xml文件中配置的初始化参数
		String initParam1 = filterConfig.getInitParameter("name");
		String initParam2 = filterConfig.getInitParameter("like");
		// 返回过滤器的所有初始化参数的名字的枚举集合。
		Enumeration<String> initParameterNames = filterConfig.getInitParameterNames();
		System.out.println(filterName);
		System.out.println(initParam1);
		System.out.println(initParam2);
		while (initParameterNames.hasMoreElements()) {
			String paramName = (String) initParameterNames.nextElement();
			System.out.println(paramName);
		}
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
		System.out.println("FilterDemo02执行前!!!");
		chain.doFilter(request, response); // 让目标资源执行,放行
		System.out.println("FilterDemo02执行后!!!");
	}

	@Override
	public void destroy() {
		System.out.println("----过滤器销毁----");
	}
}

十一、多个 Servlet 调用

1、重定向解决方案

2、转发解决方案

十二、多个 Servlet 共享数据

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

  • 20
    点赞
  • 120
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值