1.Filter
[1] Filter简介
Filter是JavaWeb中三大组件之一Servlet、Filter、Listener
Filter直译过来就叫过滤器
Filter是用来过滤请求的
Filter在JavaEE中实际上就是一个接口
Filter的作用
1 Filter可以在请求到达目标资源(JSP、Servlet、Html、CSS)之前来拦截请求
2 Filter可以放行请求,使请求到达目标资源
3 Filter可以在响应到达浏览器之前做一些处理
[2] Filter的HelloWorld
1.创建一个类并实现Filter接口
2.在web.xml配置文件中注册Filter
<filter>
<filter-name>别名</filter-name>
<filter-class>全类名</filter-class>
可以添加初始化参数标签
</filter>
<filter-mapping>
<filter-name>别名</filter-name>
<url-pattern>要拦截的地址</url-pattern>
</filter-mapping>
[3] Filter的生命周期
Filter的对象从被创建到被销毁的过程
生命周期方法:
构造器:
Filter的构造器在服务器一启动时就被调用,也就是Filter的实例在服务器启动时创建。
init()方法:
init在构造器调用之后马上被调用,用来做一些初始化操作。
服务器在调用init方法时,会传过来一个FilterConfig对象,该对象的作用和ServletConfig差不多
可以获取到Filter的别名
可以获取到当前Filter的初始化参数
可以获取到ServletContext对象
doFilter()方法:
doFilter方法在Filter每次拦截请求时都会调用,是Filter中的主要方法
服务器在调用doFilter方法时会传来这么几个参数:
ServletRequest
作用和Servlet中的一样,封装请求报文
ServletResponse
作用和Servlet中的一样,封装了响应报文
FilterChain
对象就是用来放行请求:
chain.doFilter(request,response);
destroy()方法:
destroy在项目卸载之前调用,用来在Filtert对象销毁之前做一些收尾工作
url-pattern的规则:
url-pattern用来指定需要Filter拦截的请求的地址
精确匹配:
访问地址和url-pattern的地址要一模一样,Filter才会起作用
例子 : /1.jsp
只有访问我们项目根目录下的1.jsp时才会调用Filter
在一个filter-mapping可以写多个url-pattern
路径匹配
访问的地址是在url-pattern的地址所配置的路径之下,Filter才会起作用。
例子 : /hello/*
只要访问项目根目录下的hello下的资源就会调用Filter
例子 : /*
/* 会拦截发送到当前项目的所有请求
后缀匹配:
访问的路径和url-pattern配置的后缀名一致时,Filter才会起作用
例子 : *.abc
只要访问以abc结尾的路径时,才会调用Filter
例子:*.jsp
会拦截所有以jsp为后缀的请求
我们所配置url-pattern必须符合以上规则。
Servlet的url-pattern同样适用以上规则,url-pattern中的/表示:项目根目录。!!!
[4] Filter的执行顺序
- 我们可以为一个目标资源设置多个Filter,这样多个Filter就会组成一个Filter链的结构,
如果我们在Filter中去调用chain.doFilter()如果Filter之后还有Filter,就相当于调用了下一个Filter的doFilter方法
如果Filter之后没有其他的Filter了,调用chain.doFilter相当于调用了目标资源的service()方法
如果Filter的顺序是A B C , 则拦截请求时的顺序就是A B C ,而响应回到浏览器时的拦截顺序是 C B A
- 多个Filter的执行顺序是由谁决定的呢?
Filter的执行顺序,web.xml配置文件中filter-mapping的上下顺序执行,我们可以通过调整filter-mapping的顺序,来调整Filter的执行顺序
注意filter-mapping不能在其对应的filter标签的上边
拦截Servlet的两种方式:
<servlet>
<servlet-name>HelloServlet</servlet-name>
<servlet-class>com.atguigu.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>HelloServlet</servlet-name>
<url-pattern>/HelloServlet</url-pattern>
</servlet-mapping>
- 当使用一个Filter去拦截一个Servlet时,可以将Filter的url-pattern和Servleturl-pattern设置成一样的即可
<filter-mapping>
<filter-name>CFilter</filter-name>
<url-pattern>/HelloServlet</url-pattern>
</filter-mapping>
- 也可以使用在filter-mapping中设置一个servlet-name标签,将该标签设置为和要拦截的Servlet的name一致
CFilter
HelloServlet
禁止浏览器缓存所有动态页面的过滤器:
有 3 个 HTTP 响应头字段都可以禁止浏览器缓存当前页面,它们在 Servlet 中的示例代码如下:
response.setDateHeader("Expires",-1);
response.setHeader("Cache-Control","no-cache");
response.setHeader("Pragma","no-cache");
并不是所有的浏览器都能完全支持上面的三个响应头,因此最好是同时使用上面的三个响应头。
Expires数据头:值为GMT时间值,为-1指浏览器不要缓存页面
Cache-Control响应头有两个常用值:
no-cache指浏览器不要缓存当前页面。
max-age:xxx指浏览器缓存页面xxx秒。
[5] 登录练习
- 准备两个页面
login.jsp
success.jsp
- 创建两个Filter
UsernameFilter –> 检查用户名是否正确(是否为admin)
如果正确,则放行请求
如果错误,设置一个错误消息,然后转发到login.jsp重新输入
PasswordFilter –> 检查密码是否正确 (是否为123123)
如果正确,则放行请求
如果错误,设置一个错误消息,然后转发到login.jsp重新输入
[6] dispatcher
Filter默认只会拦截直接发送到目标资源的请求,而向转发到目标资源的请求他是不会拦截的。
如果希望Filter可以拦截转发到目标资源的请求,可以在filter-mapping中的dispatcher标签中配置
<dispatcher>REQUEST</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>ERROR</dispatcher>
- dispatcher有四个可选值:dispatcher可以多选。
REQUEST
指只会拦截直接发送到目标资源的请求,默认值,如果被配置dispatcher则默认就是该值
一旦指定了dispatcher则以指定的属性为准
FORWARD
- 会拦截所有通过转发来访问目标资源的请求。
INCLUDE
- 会拦截所有通过动态包含访问目标资源的请求。
ERROR
- 会拦截在web.xml配置文件中的错误页面
404
/2.jsp
[7] HttpFilter
public abstract class HttpFilter implements Filter {
private FilterConfig filterConfig;
@Override
public void init(FilterConfig filterConfig) throws ServletException {
this.filterConfig = filterConfig;
this.init();
}
public void init()throws ServletException{
}
public abstract void doFilter(HttpServletRequest request, HttpServletResponse response,
FilterChain chain) throws IOException, ServletException;
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) request;
HttpServletResponse resp = (HttpServletResponse) response;
this.doFilter(req, resp, chain);
}
@Override
public void destroy() {
}
}
我们一不小心讲了一种设计模式
装饰者模式
装饰者模式指的就是通过一个包装类去包装原有的对象,从而对原有对象的方法进行修改。
当我们需要扩展某个对象的某个功能时,但是又不能去修改该对象的源代码,还有就是不需要永久修改该方法,
这时我们就可以使用装饰者模式,创建一个包装类,包装要扩展或修改的对象。
- 装饰者模式使用步骤:
1.创建一个包装类,包装类要求要和被包装类有相同的接口或直接或者间接父类。
2.包装类中要有一个被包装类的引用
3.在包装类中创建一个有参数的构造器,
并通过构造器将被包装类的实例传进来。
不能提供无参构造器
4.不需要修改方法可以直接通过被包装对象间接实现。
5.修改我们需要扩展或修改的方法。
6.我们在使用包装类时,需要用包装类将被包装类包装。(即包装类在实例化的时候,需要一个被包装类的对象,并将其作为包装类的构造器的参数传递进来。)
当某个对象的方法不适应业务需求时,通常有2种方式可以对方法进行增强:
编写子类,覆盖需增强的方法
使用Decorator设计模式对方法进行增强
疑问:在实际应用中遇到需增强对象的方法时,到底选用哪种方式呢?
没有具体的定式,不过有一种情况下,必须使用Decorator设计模式:即被增强的对象,开发人员只能得到它的对象,无法得到它的class文件。
比如request、response对象,开发人员之所以在servlet中能通过sun公司定义的HttpServletRequest\response接口去操作这些对象,是因为Tomcat服务器厂商编写了request、response接口的实现类。web服务器在调用servlet时,会用这些接口的实现类创建出对象,然后传递给servlet程序。
此种情况下,由于开发人员根本不知道服务器厂商编写的request、response接口的实现类是哪个?在程序中只能拿到服务器厂商提供的对象,因此就只能采用Decorator设计模式对这些对象进行增强。
Decorator设计模式的实现
1.首先看需要被增强对象继承了什么接口或父类,编写一个类也去继承这些接口或父类。
2.在类中定义一个变量,变量类型即需增强对象的类型。
3.在类中定义一个构造函数,接收需增强的对象。
4.覆盖需增强的方法,编写增强的代码。
Servlet API 中提供了一个request对象的Decorator设计模式的默认实现类HttpServletRequestWrapper , (HttpServletRequestWrapper 类实现了request 接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的 request 对象的对应方法)以避免用户在对request对象进行增强时需要实现request接口中的所有方法。