一.过滤器概述
------------------------------------------------ 1.1.什么是过滤器? Servlet技术规范中, 定义了Servlet、Filter、Listener三门技术, 其中Filter也叫做过滤器,通过过滤器技术,开发人员可以实现用户在访问某个资源之前或之后,对访问的请求和响应进行拦截,从而做一些相关的处理。 过滤器: ◇ 所谓的过滤器, 就是拦截用户对资源的访问 ◇ 一个过滤器可以拦截多个资源, 一个资源也可以配置多个过滤器进行拦截 ◇ 其实所谓的拦截, 就是将代表请求的request对象和代表响应的response对象拦截下来, 拦截下来后: ◇ 控制是否允许访问 -- 用户登陆之后才能查看自己的订单页面 ◇ 在访问资源之前或之后做一些处理 比如: 全站乱码解决 ... =================================================== 二.开发过滤器 ------------------------------------------------ 2.1.开发过滤器的步骤 Servlet API中提供了一个Filter接口, 开发web应用时, 如果编写一个类实现了这个接口, 则这个类就是一个过滤器 (1) 写一个类实现Filter接口, 并实现其中的方法 (2) 在web应用的web.xml中配置过滤器 ------------------------------------------------ ~~2.2.Filter生命周期: 当服务器启动时, web应用加载后会立即创建出当前web应用中的所有的Filter对象, 创建出来后, 立即调用init方法进行初始化出操作. 从此以后这个Filter对象一直驻留在内存中为后续所拦截的请求服务, 每次过滤到对资源的访问时, 都会执行doFilter这个方法进行拦截处理, 直到服务器关闭或者web应用移出容器为止, 随着web应用的销毁, 过滤器也跟着销毁, 在销毁之前会调用destroy方法执行善后的处理. ------------------------------------------------ 2.3.配置过滤器 <filter> -- 配置一个过滤器 <filter-name>FilterDemo1</filter-name> -- 过滤器的名字 <filter-class>cn.tedu.FilterDemo1</filter-class> -- 过滤器处理类的全路径名 </filter> <filter-mapping> -- 为指定的过滤器配置要拦截的路径, 一个过滤器可以配置多个<filter-mapping> <filter-name>FilterDemo1</filter-name> -- 过滤器的名字 <servlet-name>ServletDemo1</servlet-name> -- 拦截哪个名字的Servlet, 可以配置多个 <url-pattern>/servlet/*</url-pattern> -- 要拦截的路径, 路径的写法和Servlet的<url-pattern>写法一致, 可以配置多个 <dispatcher>REQUEST</dispatcher> -- 配置拦截哪种方式的对资源的访问, 可以取值为REQUEST/FORWARD/INCLUDE/ERROR REQUEST:默认,普通请求,最常用 FORWARD:所拦截的资源是通过请求转发访问的 INCLUDE:所拦截的资源是通过页面包含访问的 ERROR:所拦截的资源通过异常机制访问的 </filter-mapping> ------------------------------------------------ 2.4.Filter中的方法介绍 -------------------------------------------- init(FilterConfig filterConfig) FilterConfig -- 代表当前Filter在web.xml中配置信息的对象 通过这一对象可以获取当前过滤器在web.xml配置的初始化参数 通过这一对象可以获取代表当前web应用的ServletContext对象 获取初始化参数: getInitParameter(String name); getInitParameterNames() 获取ServletContext对象 getServletContext(); -------------------------------------------- doFilter(request, response, FilterChian filterChian) FilterChian -- 过滤器链 一个web资源可以被多个过滤器所拦截, 多个过滤器拦截的顺序是按照Filter在web.xml中配置的<filter-mapping>的顺序执行的.这多个过滤器按照拦截的顺序就组成了一个拦截器链, 用FilterChian表示. 如果一个过滤器处理完所拦截的请求后, 想要执行后面的拦截器, 则可以调用FilterChian上doFilter方法, 表示放行过滤器, 接着执行下一个节点 如果下一个节点仍然是过滤器, 则接着进行过滤, 执行的过程同上 如果没有后续的过滤器, 则执行真正的资源处理这次请求 -------------------------------------------- destroy() 略 =================================================== 三.过滤器的应用 ------------------------------------------------ 3.1.全站乱码解决过滤器 (1).创建EncodingFilter过滤器类, 实现过滤器接口(Filter) 详细代码参考: EncodingFilter.java (2).在web.xml中配置过滤器 <!-- 配置全站乱码解决过滤器 --> <filter> <filter-name>EncodingFilter</filter-name> <filter-class>cn.tedu.filter.EncodingFilter</filter-class> </filter> <filter-mapping> <filter-name>EncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> ------------------------------------------------ 3.2.自动登陆过滤器实现 (1).创建AutoLoginFilter过滤器类, 实现过滤器接口(Filter) 详细代码参考: AutoLoginFilter.java (2).在web.xml中配置过滤器 <!-- 配置自动登陆过滤器 --> <filter> <filter-name>AutoLoginFilter</filter-name> <filter-class>cn.tedu.filter.AutoLoginFilter</filter-class> </filter> <filter-mapping> <filter-name>AutoLoginFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> (3).在LoginServlet中, 实现30天自动登陆, 将用户名和密码保存进Cookie if("true".equals(request.getParameter("autologin"))){ //实现30天自动登陆 Cookie cookie = new Cookie("autologin", username+":"+password); cookie.setPath(request.getContextPath()+"/"); cookie.setMaxAge(3600*24*30); response.addCookie(cookie); }===================================================四.//案例--装饰者模式在乱码处理中的应用public class EncodingFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { System.out.println("全站乱码解决过滤器初始化成功..."); } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //1.解决响应正文乱码 response.setContentType("text/html;charset=utf-8"); //2.解决请求参数乱码 -- (利用装饰设计模式对request对象进行包装) HttpServletRequest myReq = new MyHttpServletRequest((HttpServletRequest)request); //3.放行过滤器 chain.doFilter(myReq, response); } public void destroy() { }}class MyHttpServletRequest extends HttpServletRequestWrapper{ //将request对象保存在类的内部 private HttpServletRequest request; //定义flag, 控制getParameterMap()方法中map的遍历次数 private boolean flag = true; public MyHttpServletRequest(HttpServletRequest request) { super(request);//这行代码千万不要省写!!! this.request = request; } public String getParameter(String name) { return getParameterValues(name) == null ? null : getParameterValues(name)[0]; } public String[] getParameterValues(String name) { return (String[]) getParameterMap().get(name); } public Map getParameterMap() { try { String method = request.getMethod(); if("POST".equals(method)){//--POST提交 request.setCharacterEncoding("utf-8"); return request.getParameterMap(); }else if("GET".equals(method)){ //手动编解码解决乱码问题! Map<String, String[]> map = request.getParameterMap(); if(flag){ for(Map.Entry<String, String[]> entry : map.entrySet()){ String[] vs = entry.getValue(); for(int i=0; i<vs.length; i++){ vs[i] = new String(vs[i].getBytes("iso8859-1"), "utf-8"); } } flag = false; } return map; }else{ return request.getParameterMap(); } } catch (UnsupportedEncodingException e) { e.printStackTrace(); throw new RuntimeException(e); } }}=================================================
五.装饰类详解
1. request继承结构
ServletRequest(接口)
|-- HttpServletRequest(接口)
|-- 匿名实现类(xxx) 实例: request对象
2. ServletRequestWrapper装饰类
request对象 --> 被装饰者
ServletRequestWrapper--> 装饰类
1.ServletRequestWrapper装饰类 和 被装饰者(request对象)所属的类(xxx)实现了同一个接口(ServletRequest)
2.提供了构造方法允许将被装饰者传入并保存在了类的内部
3.对于不想改造的方法直接调用已有对象上的方法, 对于想要改造的方法直接进行改造(没有对任何方法进行改造), 如:
3. HttpServletRequestWrapper装饰类
request对象 --> 被装饰者
HttpServletRequestWrapper -- 装饰类
HttpServletRequestWrapper类继承了ServletRequestWrapper装饰类类, 所以HttpServletRequestWrapper也是一个装饰类!!
HttpServletRequestWrapper类没有直接去包装request对象, 而是先将当前构造方法中的request对象传给父类(ServletRequestWrapper), 让父类进行包装, 再继承父类中包装后的方法。
而对于自身独有的方法, 自己再进行包装: 通过父类提供的方法(super.getRequest()) 获取 包装后的request对象, 并强制转型为 HttpServletRequest, 并通过提供 _getHttpServletRequest 方法, 方便在当前类的内部使用, 代码如下:
对于HttpServletRequestWrapper类中所有的方法, 直接调 super.getRequest() 对象 -- 即被父类包装后的request对象上的方法
也就是说, 对于HttpServletRequestWrapper装饰类, 是向将自己构造方法中的request对象传给父类(方便父类进行包装), 再通过super.getRequest(); 获取父类中包装的request对象(目的是保证自己和父类包装的是同一个request)
接下来对内部的方法进行包装, 即HttpServletRequestWrapper类中的方法分为两类: 第一类是通过父类继承过来的(父类对于这行方法已经进行包装), 第二类是自己独有的方法, 在自身类的内部进行包装!!