文章目录
在容器调用Servlet的
service()
的方法钱,Servlet其实并不会知道有请求的到来,而在service()
方法执行后,容器真正对浏览器进行HTTP响应之前,浏览器也不知道Servlet真正响应是什么。过滤器(Filter
)正如其名称所示,它介于Servlet之前,可拦截浏览器对Servlet的请求,也可以改变Servlet对浏览器的响应。 其实说白了,过滤器就是应用程序的一个额外的组件,为了方便使用并且不改变Servlet源代码,比如用户验证,字符替换,压缩这类的需求,你可能只是暂时的需要这类需求,但是过一段时间又不需要了,如果直接在Servlet中改动源码,那么就太麻烦了。因此此时就需要设置一个独立的组件,在使用的时候直接引用,不需要的时候直接删除即可,这就是过滤器的必要。
想要实现过滤器,那么需要实现
Filter
接口,这个接口中有三个必须实现的方法,分别为init()
,doFilter()
,destroy()
。init(FilterConfig config)
这是一个初始化方法,其中的参数可以获取定义的初始值,这个在后面会详细说destroy()
这个是销毁方法doFilter(HttpServletRequest request,HttpServletResponse response,FilterChain chain)
这是主要的方法,用来执行过滤的作用。当请求来到了web容器中,容器发现了调用Servlet的service()
方法之前可以应用某过滤器的时候就会调用该过滤器的doFilter()
方法。就是在doFilter()方法中进行了service()方法的前置处理,而后根据是否调用FilterChain
中的doFilter()
决定是否执行下一个过滤器,如果没有那么就执行第一个过滤器。 如果执行了FilterChain的doFilter()
方法,那么就会执行下一个过滤器,如果没有就调用指定的Servlet的service()
方法。
service()
方法之前进行的处理,就是Servlet还没有接受到请求的时候,后置处理就是在Servlet执行过service()方法之后,就是Servlet已经处理完请求之后。因此FilterChain
的doFilter()
方法就将过滤器处理分为了前置处理和后置处理,在调用FilterChain
的doFilter()
方法之前的都是对Servlet的前置处理,也就是说这时候Servlet并不知道此时有请求过来,而在其之后的都是对Servlet的后置处理。doFilter(HttpServletRequest request,HttpServletResponse response,FilterChain chain)
{
//service()的前置处理
chain.doFilter(request,response);
//service()的后置处理
}
FilterChain
执行后会一堆栈顺序返回,就是说如果有多个Filter
,那么就先按照顺序执行chain.doFilter(request,response)
之前的代码,即是先前置处理,然后入栈,这样一直到执行到最后一个Filter
,之后就从栈顶开始执行chain.doFilter()
的方法之后的代码,即是后置处理。总的来说就是先执行前置处理,然后入栈,待全部执行完毕之后再从栈顶开始后置处理的代码。
Filter
的doFilter
的方法中的request
,response
和Servlet的doGet()
和doPost()
方法中的是一样的,即是可以设置属性,可以得到表单提交的值,总之是一样的。
package com;
import java.io.IOException;
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.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
public class FilterDemo1 implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
// 根据request获取表单的用户名和密码
String username = request.getParameter("username");
String password = request.getParameter("password");
// 如果用户名和密码正确
if ("陈加兵".equals(username) && "123456".equals("password")) {
System.out.println("用户名或者密码错误,请重新输入");
}
//继续执行下一个过滤器,如果有就执行
chain.doFilter(request, response);
//当所有过滤器的前置处理都执行完毕才执行这个语句
System.out.println("Servlet已经执行完毕");
}
public void init(FilterConfig config) throws ServletException {
}
}
web.xml
中设置过滤器,设置的方式如下:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" id="WebApp_ID" version="2.5">
<display-name>web2</display-name>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.htm</welcome-file>
<welcome-file>index.jsp</welcome-file>
<welcome-file>default.html</welcome-file>
<welcome-file>default.htm</welcome-file>
<welcome-file>default.jsp</welcome-file>
</welcome-file-list>
<!-- 定义FilterDemo1的过滤器 -->
<filter>
<!--设置过滤器文件的名字-->
<filter-name>FilterDemo1</filter-name>
<!--设置过滤器类所在的路径,具体到包名-->
<filter-class>com.FilterDemo1</filter-class>
</filter>
<filter-mapping>
<filter-name>FilterDemo3</filter-name>
<!--设置作用的url,即是Demo9这个Servlet应用这个过滤器-->
<url-pattern>/Demo9</url-pattern>
<!-- <servlet-name>Demo9</servlet-name> 这个标签和上面的<url-pattern>是一个效果,直接指明应用的Servlet的名称 -->
<!--Demo1也应用这个过滤器-->
<servlet-name>Demo1</servlet-name>
</filter-mapping>
<!-- 定义FilterDemo1的过滤器 -->
<!-- 定义FilterDemo2的过滤器 -->
<filter>
<filter-name>FilterDemo2</filter-name>
<filter-class>com.FilterDemo2</filter-class>
</filte>
<filter-mapping>
<filter-name>FilterDemo2</filter-name>
<!-- Demo1这个Servlet文件也应用FilterDemo2这个过滤器,那么当请求Demo的时候要按照定义的先后顺序先执行FilterDemo1这个过滤器 -->
<url-pattern>/Demo1</url-pattern>
</filter-mapping>
<!-- 定义FilterDemo2的过滤器 -->
</web-app>
<filter-mapping>
中定义多个Servlet文件,表示多个Servlet都应用于这个过滤器这个和
ServletConfig
一样的都存在初始参数,当然定义的方式也是不尽相同,都是在web.xml
中定义的,如下:
<filter>
<filter-name>FilterDemo3</filter-name>
<filter-class>com.FilterDemo3</filter-class>
<!--直接在filter下可以设置初始参数,当然我们可以在过滤器中获取参数-->
<init-param>
<param-name>username</param-name>
<param-value>陈加兵</param-value>
</init-param>
<init-param>
<param-name>password</param-name>
<param-value>123456</param-value>
</init-param>
</filter>
直接利用其中的
init(FilteConfig config)
获取初始化参数
package com;
import java.io.IOException;
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.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
public class FilterDemo1 implements Filter {
public String username;
public String password;
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
}
//直接在init方法中利用FilterConfig的方法获取参数的值
public void init(FilterConfig config) throws ServletException {
//获取初始值
username=config.getInitParameter("username");
password=config.getInitParameter("password");
}
}
当我们直接请求Servlet文件的url或者表单提交的时候使用的都是浏览器默认发出的请求,这个是可以触发过滤器的。但是如果是那些重定向(
sendirect
)或者转发包含(forward
,include
)就不会默认触发,因此我们需要在web.xml
设置触发的时机,定义如下:
<filter-mapping>
<filter-name>FilterDemo1</filter-name>
<url-pattern>/Demo1</url-pattern>
<dispatcher>REQUEST</dispatcher> <!--默认的-->
<dispatcher>FORWARD</dispatcher> <!--forward-->
<dispatcher>INCLUDE</dispatcher> <!--include-->
<dispatcher>ERROR</dispatcher> <!--error -->
</filter-mapping>