一、Filter的部署——注册Filter
<filter> <filter-name>testFitler</filter-name> <filter-class>org.test.TestFiter</filter-class> <init-param> <param-name>word_file</param-name> <param-value>/WEB-INF/word.txt</param-value> </init-param> </filter>
1、<filter-name>用于为过滤器指定一个名字,该元素的内容不能为空。
2、<filter-class>元素用于指定过滤器的完整的限定类名。
3、<init-param>元素用于为过滤器指定初始化参数,它的子元素<param-name>指定参数的名字,<param-value>指定参数的值。在过滤器中,可以使用FilterConfig接口对象来访问初始化参数。
二、Filter的部署——映射Filter
1、<filter-mapping>元素用于设置一个 Filter 所负责拦截的资源。一个Filter拦截的资源可通过两种方式来指定:Servlet名称和资源访问的请求路径。
2、<filter-name>子元素用于设置filter的注册名称。该值必须是在<filter>元素中声明过的过滤器的名字。
3、<url-pattern>设置 filter 所拦截的请求路径(过滤器关联的URL样式)
4、<servlet-name>指定过滤器所拦截的Servlet名称。
5、<dispatcher>指定过滤器所拦截的资源被 Servlet 容器调用的方式,可以是REQUEST,INCLUDE,FORWARD和ERROR之一,默认REQUEST。用户可以设置多个<dispatcher> 子元素用来指定 Filter 对资源的多种调用方式进行拦截。
6、<dispatcher> 子元素可以设置的值及其意义:
(1)REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。
(2)INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。
(3)FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。
(4)ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。注意:一定要在web.xml文件声明错误代码或类型
<error-page> <error-code>500</error-code> <location>/sys_500.jsp</location> </error-page> <filter-mapping> <filter-name>MappingFilter</filter-name> <url-pattern>/*</url-pattern> <dispatcher>ERROR</dispatcher> </filter-mapping>
三、装饰设计模式
1、需求:当某个类的某个方法不适应当前业务的需要
2、思路:(1)扩展父类的可供扩展的方法,可以使有,但不优;(2)装饰设计模式(推荐)
3、开发步骤:
(1)首先看需要被增强对象继承了什么接口或父类,编写一个类也去继承这些接口或父类或被增强对象本身。
(2)在类中定义一个变量,变量类型即需增强对象的类型。
(3)在类中定义一个构造函数,接收需增强的对象。
(4)覆盖需增强的方法,编写增强的代码。
(5)对于不想增强的方法,直接调用被增强对象的方法。
4、案例:对BufferedReader进行装饰
package com.gnnuit.web.decorator; import java.io.BufferedReader; import java.io.IOException; //对BufferedReader进行装饰 public class MyBufferedRead { private BufferedReader reader; private int no; public MyBufferedRead(BufferedReader reader) { this.reader = reader; } // 重写父对象的方法 public String readLine() throws IOException { String line = reader.readLine(); if (line != null) { no++; line = no + ":" + line; } return line; } // 真接使用父对象的方法 public void close() throws Exception { reader.close(); } }
四、 request对象的增强案例
1、使用Decorator模式包装request对象,完全解决get、post请求方式下的乱码问题。
(1)MyRequest.java
package com.gnnuit.web.decorator; import java.io.UnsupportedEncodingException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; public class MyRequest extends HttpServletRequestWrapper { private HttpServletRequest request; public MyRequest(HttpServletRequest request) { super(request); this.request = request; } @Override public String getParameter(String name) { String value = null; String method = request.getMethod(); if ("GET".equals(method)) { value = request.getParameter(name); byte[] buf; try { buf = value.getBytes("ISO8859-1"); value = new String(buf, "UTF-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else if ("POST".equals(method)) { try { request.setCharacterEncoding("UTF-8"); value = request.getParameter(name); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } return value; } }
(2)EncodingFilter.java
package com.gnnuit.web.filter; 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.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.gnnuit.web.decorator.MyRequest; public class EncodingFilter implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletRequest servletRequest = (HttpServletRequest) request; HttpServletResponse servletResponse = (HttpServletResponse) response; MyRequest myRequest = new MyRequest(servletRequest); chain.doFilter(myRequest, servletResponse); } @Override public void init(FilterConfig arg0) throws ServletException { } }
2、使用Decorator模式包装request对象,实现html标签转义功能(Tomcat服务器中提供了转义html标签的工具类)。
(1)MyRequest.java
package cn.itcast.web.decorator; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; //对HttpServletRequest对象包装/装饰 public class MyRequest extends HttpServletRequestWrapper{ private HttpServletRequest request; public MyRequest(HttpServletRequest request) { super(request); this.request = request; } //重写父类的方法 public String getParameter(String name) {//表单项的名字 String value = null; //取得客户端的请求方式[GET/POST] String method = request.getMethod(); if("GET".equals(method)){ try { value = request.getParameter(name);//乱码 byte[] buf = value.getBytes("ISO8859-1"); value = new String(buf,"UTF-8");//正码 } catch (Exception e) { e.printStackTrace(); } }else if("POST".equals(method)){ try { request.setCharacterEncoding("UTF-8"); value = request.getParameter(name);//正码 } catch (Exception e) { e.printStackTrace(); } } value = filter(value); return value; } //转义方法 public String filter(String message) { if (message == null) return (null); char content[] = new char[message.length()]; message.getChars(0, message.length(), content, 0); StringBuffer result = new StringBuffer(content.length + 50); for (int i = 0; i < content.length; i++) { switch (content[i]) { case '<': result.append("<"); break; case '>': result.append(">"); break; case '&': result.append("&"); break; case '"': result.append("""); break; default: result.append(content[i]); } } return (result.toString()); } }
五、response增强案例——压缩响应
Servlet API 中提供了response对象的Decorator设计模式的默认实现类HttpServletResponseWrapper , (HttpServletResponseWrapper类实现了response接口中的所有方法,但这些方法的内部实现都是仅仅调用了一下所包装的的 response对象的对应方法)以避免用户在对response对象进行增强时需要实现response接口中的所有方法。
应用HttpServletResponseWrapper对象,压缩响应正文内容。思路:
1、通过filter向目标页面传递一个自定义的response对象。
2、在自定义的response对象中,重写getOutputStream方法和getWriter方法,使目标资源调用此方法输出页面内容时,获得的是我们自定义的ServletOutputStream对象。
3、在我们自定义的ServletOuputStream对象中,重写write方法,使写出的数据写出到一个buffer中。
4、当页面完成输出后,在filter中就可得到页面写出的数据,从而我们可以调用GzipOuputStream对数据进行压缩后再写出给浏览器,以此完成响应正文件压缩功能。
5、完整代码:
(1)ShowServlet.java
package com.gnnuit.web.servlet; import java.io.IOException; import javax.servlet.ServletException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class ShowServlet extends HttpServlet { private static final long serialVersionUID = 1L; public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { ServletOutputStream sos = response.getOutputStream(); sos.write("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" .getBytes()); } }
(2)MyResponse.java
package com.gnnuit.web.decorator; import java.io.ByteArrayOutputStream; import java.io.IOException; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; //对response对象的装饰/包装 public class MyResponse extends HttpServletResponseWrapper { private HttpServletResponse response; // 缓存 private ByteArrayOutputStream bout = new ByteArrayOutputStream(); public MyResponse(HttpServletResponse response) { super(response); this.response = response; } // 重写父类方法,目的是将字节输出到缓存中去[字节] @Override public ServletOutputStream getOutputStream() throws IOException { return new MyServletOutputStream(bout); } // 取得缓存中的数据 public byte[] getBuffer() { return bout.toByteArray(); } } // 带有缓存功能ServletOutputStream class MyServletOutputStream extends ServletOutputStream { private ByteArrayOutputStream bout; public MyServletOutputStream(ByteArrayOutputStream bout) { this.bout = bout; } @Override public void write(int b) throws IOException { } public void write(byte[] bytes) throws IOException { // 将字节数组的内容写入缓存 bout.write(bytes); // 确保所有字节数组内容进入缓存 bout.flush(); bout.close(); } }
(3)GzipFilter.java
package com.gnnuit.web.filter; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.zip.GZIPOutputStream; 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.HttpServletResponse; import com.gnnuit.web.decorator.MyResponse; public class GzipFilter implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse servletResponse = (HttpServletResponse) response; MyResponse myResponse = new MyResponse(servletResponse); chain.doFilter(request, myResponse); // 取得缓存中的内容 byte[] data = myResponse.getBuffer(); System.out.println("压缩前:" + data.length); // 进行压缩 ByteArrayOutputStream bout = new ByteArrayOutputStream(); GZIPOutputStream gout = new GZIPOutputStream(bout); gout.write(data); gout.flush(); gout.close(); data = bout.toByteArray(); System.out.println("压缩后:" + data.length); // 通知浏览器接收的是一个压缩性数据和长度 servletResponse.setHeader("content-encoding", "gzip"); servletResponse.setHeader("content-length", data.length + ""); // 以字节方式真正输出到浏览器 response.getOutputStream().write(data); } @Override public void init(FilterConfig arg0) throws ServletException { } }
六、实用案例——缓存数据到内存
对于页面中很少更新的数据,例如商品分类,为避免每次都要从数据库查询分类数据,因此可把分类数据缓存在内存或文件中,以此来减轻数据库压力,提高系统响应速度。
思路:
1 得到用户访问资源的URI。
2 检查缓存中是否有该页面。
3 如果有则取出缓存中的数据显示给浏览器。
4 否则让目标资源执行,并捕获目标资源的输出。
5 将目标资源的数据以URI为关键字存到缓存中。
6 向浏览器输出目标资源。
代码如下:
CacheFilter.java
package com.gnnuit.web.filter; import java.io.IOException; import java.util.HashMap; import java.util.Map; 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.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import com.gnnuit.web.decorator.MyResponse; public class CacheFilter implements Filter { private Map<String, byte[]> cache = new HashMap<String, byte[]>(); @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse servletResponse = (HttpServletResponse) response; HttpServletRequest servletRequest = (HttpServletRequest) request; // NO1:取得客户端访问的资源路径 String uri = servletRequest.getRequestURI(); // 根据uri去缓存中查询对应的页面资源有没有 byte[] data = cache.get(uri); // NO3:如果没有 if (data == null) { // 将请求和响应放到web资源中 MyResponse myResponse = new MyResponse(servletResponse); chain.doFilter(servletRequest, myResponse); // NO4:将刚取得的数据放入到缓存中,便于下次重用 data = myResponse.getBuffer(); cache.put(uri, data); System.out.println("从服务端取得资源"); // NO5:如果有,直接从缓存中了得数据 } // NO6:向浏览器输出数据 servletResponse.getOutputStream().write(data); } @Override public void init(FilterConfig arg0) throws ServletException { } }
七、总结Filter和Servlet
1、Filter通常完成一些非核心的业务流程控制;Servlet通常完成一些核心的业务流程控制。
2、Filter通常完成一些对Servlet的请求和响应的预先处理控制;Servlet却不行。
3、Filter和Servlet是一个互补的技术,而不是替代技术。