Better

  • 主页
  • 随笔
所有文章 友链 关于我

Better

  • 主页
  • 随笔

javaee学习—过滤器

2020-04-25

一 、了解过滤器

什么是过滤器?

  • 一种Java组件 ;
  • 可被用来截获并处理发送到servlet之前的请求 ;
  • 可被用来处理servlet运行完而发送回客户端之前 的响应 ;
  • 在DD中配置容器何时调用过滤器。

过滤器有什么作用?

过滤器分为请求过滤器和响应过滤器

  • 请求过滤器
    • 执行安全检查
    • 重新格式化请求的头或体
    • 审计或记录请求的日志
  • 响应过滤器
    • 压缩响应流
    • 追加或变更响应流
    • 创建一个完全不同的响应

过滤器的特点

  • 过滤器是模块化的

    • 可以链式排列
    • 完全是自包含的
    • 在DD中配置过滤器运行的顺序
  • 过滤器与servlet相似

    • 容器对过滤器API是感知的

      • 过滤器实现Filter接口

      • 过滤器可以获得ServletContext,并能与其他过滤器连接

    • 容器管理过滤器的寿命周期

      • Init(), destroy(), doFilter()
    • 过滤器在DD中定义

      • 一个web应用可以有多个过滤器
      • 一个给定的请求可以使多个过滤器运行
      • 过滤器本身并不知道谁要调用它们,以及它的下一个是谁

二 、过滤器的寿命周期

每个过滤器必须实现Filter接口中的三个方法:

  • init():容器决定实例化一个过滤器时,该方法可以在过滤器被调用前执行一些构建任务,如获得 FilterConfig对象的引用。
  • doFilter():当容器确定该过滤器应当用于当前请求时, 调用此方法。可以在其中执行过滤器功能。有三个参数:
    • ServletRequest
    • ServletResponse
    • FilterChain
  • destroy():当容器决定移除一个过滤器实例时,调用 该方法,可以在其中执行一些清除对象的代码

FilterChain接口

过滤器是一些模块化的“构造块”,可以任何顺 序拼凑在一起,FilterChain知道过滤器的拼凑方式,FilterChain接口的doFilter()方法计算下来应调用 哪个过滤器的doFilter()方法,或者servlet的 service()方法,而Filter接口的doFilter()方法执行 实际的过滤操作。

三 、怎么编写一个过滤器

3.1 在DD中配置过滤器

1.声明过滤器

声明过滤器就是在DD中的filter标签中设置:

  • <filter-name> 过滤器的内部名字,用来找到<filter-mapping>的内容,这个属性是必须的。
  • <filter-class> 是标记了处理过滤器的java代码所在的位置,也是必须的
  • <init-param>是初始化参数,这个是可选的,比如下面这个例子初始化了用户登录的文件User.txt。在处理器里面可以获取这个文件,并进行处理。
image-20200424212052826

2. 将过滤器与打算过滤的web资源映射起来

1)定义web资源通

用URL样式定义web资源

1
2
3
4
<filter-mapping>
<filter-name>BeerRequest</filter-name> //必须的
<url-pattern>*.do</url-pattern>
</filter-mapping>

用servlet名字定义web资源

1
2
3
4
<filter-mapping>
<filter-name>BeerRequest</filter-name> //必须的
<servlet-name>AdviceServlet</servlet-name>
</filter-mapping>

2)请求转发的web资源

请求转发的web资源,可以有0到4个<dispatcher>元素。

REQUEST表示过滤器是对客户端请求的,是缺省值;

INCLUDE表示过滤器是对从include()调用的请求转发的;比如对<jsp:include>引入的页面进行处理。

FORWARD表示过滤器 是对从forward()调用的请求转发的;

ERROR表示过滤器是对错误处理 器调用的资源的

1
2
3
4
5
6
7
8
<filter-mapping>
<filter-name>MonitorFilter</filter-name>
<url-pattern>*.do</url-pattern>
<dispatcher>REQUEST</dispatcher>
<dispatcher>INCLUDE</dispatcher>
<dispatcher>FORWARD</dispatcher>
<dispatcher>ERROR</dispatcher>
</filter-mapping>

3) 组织这些映射关系来创建过滤器调用序列

所有需匹配URL样式的过滤器先排序 。

能匹配URL样式的过滤器依照其在DD中声明的顺序放置到FilterChain中。

过滤器链的排序原则:过滤器首先是根据url-pattern来判断是用哪个过滤器,如果url-pattern符合多个过滤器的时候,就根据过滤器在web.xml中配置的先后顺序来依次执行过滤器。然后根据servlet-name来判断过滤器,过滤器的顺序按照DD中声明的顺序来。

3.2 编写过滤器 (请求过滤器)

需求:编写一个请求过滤器,实现对除了首页SelectBeer.jsp之外所有通过get方法访问页面请求的过滤,当通过get方法访问页面时,自动跳转回首页SelectBeer.jsp。如:在浏览器直接访问BeerList.jsp,则自动跳转回首页。

首先需要在DD中配置filter,设置初始化参数 includePage:不被过滤的主页;redirectPath:过滤后跳转的页面。过滤的URL是对这个项目的所有网页进行过滤。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<filter>
<filter-name>GetRequest</filter-name>
<filter-class>lesson.j2ee.ex4.GetRequestFilter</filter-class>
<init-param>
<param-name>includePage</param-name><!-- 对主页面不进行过滤 -->
<param-value>/ex4/SelectBeer.jsp</param-value>
</init-param>
<init-param>
<param-name>redirectPath</param-name> <!-- 未通过跳转到主界面 -->
<param-value>/SelectBeer.jsp</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>GetRequest</filter-name>
<url-pattern>/*</url-pattern>
<dispatcher>REQUEST</dispatcher>
</filter-mapping>

然后编写过滤器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class GetRequestFilter implements Filter {
protected FilterConfig config;

@Override
public void destroy() {
this.config = null; //清除config对象

}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException {
HttpServletRequest httpreq = (HttpServletRequest) req;
HttpServletResponse httprep = (HttpServletResponse) resp;
String redirectPath = httpreq.getContextPath() + config.getInitParameter("redirectPath");//跳转路径
String includePage = config.getInitParameter("includePage");//获取DD中的初始化参数。
String method = httpreq.getMethod();//获取页面请求的方法
if(method != null) {
if(!method.equals("GET")){
chain.doFilter(req, resp);//不是get请求不进行过滤
return;
} else if (httpreq.getRequestURI().indexOf(includePage) != -1){
chain.doFilter(req, resp);//是主页也不进行过滤
return;

}else {
System.out.println("get请求");//是get请求进行跳转
httprep.sendRedirect(redirectPath);
return;
}
} else {
chain.doFilter(req, resp);//处理下一个过滤器
}

}
//实例化一个过滤器,获得FilterConfig对象的引用
@Override
public void init(FilterConfig config) throws ServletException {
this.config = config;
}

}

四 、响应过滤器

响应过滤器在servlet执行完后,可以在响应发送到客端前 ,对响应输出做一些处理。

那如何实现这一想法呢?

直接使用容器传来的Response对象是否可行?

如下图直接使用容器传来的Response对象,servlet处理完之后并不知道需要输出过滤,把响应传给客户端输出,过滤器的并没有进行过滤处理。

image-20200425110450229

4.1 包装响应对象

我们可以在过滤器中将原响应对象进行处理,使得自定义的响应对象有一个能被我们控制的输出流, 而后通过chain.doFilter()方法把它传给servlet。

也就是我们把原响应对象进行包装,使得包装之后的response对象有一个重载getOutputStream方法。

怎么使得包装之后的response对象有一个重载getOutputStream方法?

通过实现HttpServletResponse接口,重载getOutputStream方法,返回一个包装了的 ServletOutputStream。

image-20200425193700337

下面通过一个响应过滤器的例子来理解上面的概念。

4.2 请求过滤器实例

需求: 实现对页面上输出文本的大写转化,也即是网页所展示的字母都转化为大写。

1.仍然是在DD中配置

1
2
3
4
5
6
7
8
<filter>
<filter-name>UpperCase</filter-name>
<filter-class>lesson.j2ee.ex4.UpperCaseFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>UpperCase</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

2.编写响应的包装器类

创建一个“假的response”–继承HttpServletResponseWrapper类,重载getOutputStream方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public class CharResponseWrapper extends HttpServletResponseWrapper {
protected CharArrayWriter charWriter; // 将响应缓存在这个写入器中
protected PrintWriter writer;
protected boolean getOutputStreamCalled;
protected boolean getWriterCalled;

public CharResponseWrapper(HttpServletResponse response) {
super(response);
// 创建writer
charWriter = new CharArrayWriter();
}

/**
* 重载getOutputStream()方法
*/
public ServletOutputStream getOutputStream() throws IOException {
// 如果getWriter()方法被调用了,就不能调用getOutputStream()方法
if (getWriterCalled) {
throw new IllegalStateException("getWriter already called");
}
getOutputStreamCalled = true;
return super.getOutputStream();
}

/**
* 重载getWriter()方法
*/
public PrintWriter getWriter() throws IOException {
if (writer != null) {
return writer;
}
// 如果getOutputStream()方法被调用了,就不能调用方法getWriter()
if (getOutputStreamCalled) {
throw new IllegalStateException("getOutputStream already called");
}
getWriterCalled =true;
writer = new PrintWriter(charWriter);
return writer;
}

/**
* 将响应数据用字符串返回
*/
public String toString() {
String s = null;
if(writer != null){
s = charWriter.toString();
}
return s;
}

}

3.编写响应过滤器

在过滤器里面把ServletResponse的相应响应换成之前包装的响应对象。

在filter进行替换内容和实际输出。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public class UpperCaseFilter implements Filter {
protected FilterConfig config;
@Override
public void destroy() {
this.config = null;
}

@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
ServletResponse newResponse = resp;
if(req instanceof HttpServletRequest) {
//引用包装后的response对象
newResponse = new CharResponseWrapper((HttpServletResponse)resp);
}
// 执行doFilter方法,注意此处响应对象已经变化
chain.doFilter(req, newResponse);
// 以下执行响应过滤
if(newResponse instanceof CharResponseWrapper){
String text = newResponse.toString();
if(text != null){
text = text.toUpperCase();
//将输出的内容传给真正的输出流返回客户端
resp.getWriter().write(text);
}
}
}

@Override
public void init(FilterConfig config) throws ServletException {
this.config = config;

}

}

总结

响应过滤器就是在Servlet或者jsp接收到response之前,将response使用一个“假的response”替换,让servlet或者jsp的内容输出到这个“假的response”中,然后再对输出内容进行处理,最后再将整个通过真的response输出给客户端。

image-20200426112403709
赏

谢谢你请我吃糖果

  • javaee

扫一扫,分享到微信

微信分享二维码
CSS学习-CSS基础
javaee学习之自定义tag
© 2020 Better
Hexo Theme Yilia by Litten
  • 所有文章
  • 友链
  • 关于我

tag:

  • css
  • js
  • html
  • Python
  • Numpy
  • Pandas
  • vue
  • uml
  • 框架
  • c++
  • git
  • 工具
  • javaee
  • java
  • leetcode
  • 数据库
  • tensorflow
  • 工具类
  • 网页布局
  • RL
  • more
  • 算法
  • summary
  • 计算机组成原理
  • 软件测试
  • 插件
  • jsp
  • 移动端布局
  • 操作系统
  • 微信小程序
  • 计算机网络
  • hobby
  • 机器学习
  • python

    缺失模块。
    1、请确保node版本大于6.2
    2、在博客根目录(注意不是yilia根目录)执行以下命令:
    npm i hexo-generator-json-content --save

    3、在根目录_config.yml里添加配置:

      jsonContent:
        meta: false
        pages: false
        posts:
          title: true
          date: true
          path: true
          text: false
          raw: false
          content: false
          slug: false
          updated: false
          comments: false
          link: false
          permalink: false
          excerpt: false
          categories: false
          tags: true
    

  • 友情链接1
  • 友情链接2
  • 友情链接3
  • 友情链接4
  • 友情链接5
  • 友情链接6
很惭愧

只做了一点微小的工作
谢谢大家