【简记】Java Web 内幕——Filter

来源:互联网 发布:速拓软件手机 编辑:程序博客网 时间:2024/04/27 23:26

本文内容:

  • 过滤器介绍
  • 过滤器的生成过程,执行顺序
  • 案例(利用过滤器对字符编码进行处理)

为什么需要过滤器

项目开发中,经常会涉及到重复代码的实现!

如何解决:
1. 抽取重复代码,封装
2. 每个用到重复代码的地方,手动地调用!

过滤器,设计执行流程:
1. 用户访问服务器
2. 过滤器: 对Servlet请求进行拦截
3. 先进入过滤器, 过滤器处理
4. 过滤器处理完后, 再放行, 此时,请求到达Servlet/JSP
5. Servlet处理
6. Servlet处理完后,再回到过滤器, 最后在由tomcat服务器相应用户

这里写图片描述

实现一些特殊的功能。例如实现URL级别的权限访问控制、过滤敏感词汇、压缩响应信息等一些高级功能


Filter简介

Servlet API中提供了一个Filter接口,开发web应用时,如果编写的Java类实现了这个接口,则把这个java类称之为过滤器Filter。通过Filter技术,开发人员可以实现用户在访问某个目标资源之前,对访问的请求和响应进行拦截。

过滤器相关Api:

|– interface Filter 过滤器核心接口
void init(filterConfig);
初始化方法,在服务器启动时候执行;

void doFilter(request,response,filterChain);
在每个用户的请求进来时这个方法都会被调用,并在Servlet 的service 方法之前被调用。

void destroy();
销毁过滤器实例时候调用

|– interface FilterConfig
获取初始化参数信息;FilterConfig 与ServletConfig 也类似, 除了都能取到容器的环境类ServletContext 对象之外,还能获取在< filter >下配置的< init-param>参数值。

|– interface FilterChain
过滤器链参数;一个个过滤器形成一个执行链;
FilterChain 就代表当前的整个请求链,所以通过调用FilterChain . doFilter 可以将请求继续传递下去。如果想拦截这个请求, 可以不调用FilterChain.doFilter , 那么这个请求就直接返回了。所以Filter 是一种责任链设计模式。

void doFilter(ServletRequest request, ServletResponse response) ;


与Servlet的不同之处

  • 在tomcat服务器启动时就会调用filter的构造函数来创建实例(Servlet默认是当调用时才会创建实例)

filterDemo:

public class HelloFilter implements Filter{    // 创建实例    public HelloFilter(){        System.out.println("1. 创建过滤器实例");    }    @Override    public void init(FilterConfig filterConfig) throws ServletException {        System.out.println("2. 执行过滤器初始化方法");        // 获取过滤器在web.xml中配置的初始化参数        String encoding = filterConfig.getInitParameter("encoding");        System.out.println(encoding);        // 获取过滤器在web.xml中配置的初始化参数 的名称        Enumeration<String> enums =  filterConfig.getInitParameterNames();        while (enums.hasMoreElements()){            // 获取所有参数名称:encoding、path            String name = enums.nextElement();            // 获取名称对应的值            String value = filterConfig.getInitParameter(name);            System.out.println(name + "\t" + value);        }    }    // 过滤器业务处理方法: 在请求到达servlet之前先进入此方法处理公用的业务逻辑操作    @Override    public void doFilter(ServletRequest request, ServletResponse response,                         FilterChain chain) throws IOException, ServletException {        System.out.println("3. 执行过滤器业务处理方法");        // 放行 (去到Servlet)        // 如果有下一个过滤器,进入下一个过滤器,否则就执行访问servlet        chain.doFilter(request, response);        System.out.println("5. Servlet处理完成,又回到过滤器");    }    @Override    public void destroy() {        System.out.println("6. 销毁过滤器实例");    }}

web.XML中的配置

    <!-- 过滤器配置 -->    <filter>        <!--配置初始化参数-->        <init-param>            <param-name>encoding</param-name>            <param-value>UTF-8</param-value>        </init-param>        <filter-name>hello_filter</filter-name>        <filter-class>day21.a_filter_hello.HelloFilter</filter-class>    </filter>    <filter-mapping>        <filter-name>hello_filter</filter-name>        <url-pattern>/*</url-pattern>    </filter-mapping>

过滤器执行流程

这里写图片描述

这里写图片描述


对指定的请求拦截

<!-- 配置第二个过滤器 -->    <!-- 演示: 拦截指定的请求 -->    <filter>        <filter-name>hello_filter2</filter-name>        <filter-class>cn.itcast.a_filter_hello.HelloFilter2</filter-class>    </filter>    <filter-mapping>        <filter-name>hello_filter2</filter-name>        <!-- 1. 拦截所有        <url-pattern>/*</url-pattern>         -->         <!-- 2. 拦截指定的jsp          <url-pattern>/index.jsp</url-pattern>         <url-pattern>/list.jsp</url-pattern>         -->         <!-- 拦截所有的jsp         <url-pattern>*.jsp</url-pattern>          -->          <!-- 3. 根据servlet的内部名称拦截          <servlet-name>IndexServlet</servlet-name>           -->          <!-- 拦截指定的servlet           <url-pattern>/index</url-pattern>          -->          <!-- 4. 指定拦截指定的类型 -->          <url-pattern>/*</url-pattern>          < dispatcher >REQUEST</ dispatcher >          < dispatcher >FORWARD</ dispatcher >    </filter-mapping>
默认拦截的类型:(直接访问或者重定向)<dispatcher>REQUEST</dispatcher>拦截转发:          <dispatcher>FORWARD</dispatcher>拦截包含的页面(RequestDispatcher.include(/page.jsp);    对page.jsp也执行拦截)          <dispatcher>INCLUDE</dispatcher>拦截声明式异常信息:          <dispatcher>ERROR</dispatcher>

在web.xml中可以配置指定的error页


案例一:过滤器处理编码实例

几乎每一个Servlet都要涉及编码处理:处理请求数据中文问题!
【GET/POST】
每个servlet都要做这些操作,把公用的代码抽取-过滤器实现!

网页代码:

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%><!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"><html>  <head>    <title>My JSP 'index.jsp' starting page</title>    <meta http-equiv="pragma" content="no-cache">    <meta http-equiv="cache-control" content="no-cache">    <meta http-equiv="expires" content="0">      </head>  <body>    <form name="frmLogin" action="${pageContext.request.contextPath }/login" method="post">       用户名: <input type="text" name="userName"><br/>      <input type="submit" value="POST提交" >    </form>    <hr/>    <form name="frmLogin" action="${pageContext.request.contextPath }/login" method="get">       用户名: <input type="text" name="userName"><br/>      <input type="submit" value="GET提交" >    </form>  </body></html>

处理表单的Servlet:

public class LoginServlet extends HttpServlet {    public void doGet(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        // 获取请求数据        String name = request.getParameter("userName");        System.out.println("用户:" + name);    }    public void doPost(HttpServletRequest request, HttpServletResponse response)            throws ServletException, IOException {        this.doGet(request, response);    }}

过滤器:
(1)仅对post方法有效

    public void doFilter(ServletRequest req, ServletResponse res,                         FilterChain chain) throws IOException, ServletException {        System.out.println("3. 执行过滤器业务处理方法");        // 转型        final HttpServletRequest request = (HttpServletRequest) req;        HttpServletResponse response = (HttpServletResponse) res;        // 一、处理公用业务        request.setCharacterEncoding("UTF-8");                    // POST提交有效        response.setContentType("text/html;charset=UTF-8");        chain.doFilter(proxy, response);        System.out.println("5. Servlet处理完成,又回到过滤器");        }

(2)使用代理

        HttpServletRequest proxy = (HttpServletRequest) Proxy.newProxyInstance(                request.getClass().getClassLoader(),        // 指定当前使用的类加载器                new Class[]{HttpServletRequest.class},        // 对目标对象实现的接口类型                new InvocationHandler() {                    // 事件处理器                    @Override                    public Object invoke(Object proxy, Method method, Object[] args)                            throws Throwable {                        // 定义方法返回值                        Object returnValue = null;                        // 获取方法名                        String methodName = method.getName();                        // 判断:对getParameter方法进行GET提交中文处理                        if ("getParameter".equals(methodName)) {                            // 获取请求数据值【 <input type="text" name="userName">】                            String value = request.getParameter(args[0].toString());    // 调用目标对象的方法                            System.out.println(getEncoding(value));                            // 获取提交方式                            String methodSubmit = request.getMethod(); // 直接调用目标对象的方法                            // 判断如果是POST提交                            if ("POST".equals(methodSubmit)) {                                if (value != null && !"".equals(value.trim())) {                                    // 处理GET中文                                    value = new String(value.getBytes("ISO-8859-1"), "UTF-8");                                }                            }                            return value;                        } else {                            // 执行request对象的其他方法                            returnValue = method.invoke(request, args);                        }                        return returnValue;                    }                });
阅读全文
0 0