首先了解一下装饰模式
对一个类的增强,一般采取三种式
- 继承被增强的类,即实现一个子类。
- 使用动态代理处理需要增强的方法。
- 使用包装设计模式。(Java中的IO基本上都是包装设计模式)
以下是使用包装设计模式增强一个类的步骤:
- 继承需要增强的类。
- 声明需要增强有的类为自己的成员变量。
- 书写一个构造方法接收需要增强的类。
- 实现需要增强的方法。
- 实现可扩展的其他方法。
包装(装饰)设计模式是指:
假定A类是B类的包装类,那么类A与类B有同样的接口,并且类A拥有类B的的实例,类A借助类B的实例来实现接口。
学习导航
运用三:敏感词汇过滤
运用场景:在论坛网址中,过虑非法语句,如脏话,和非法词组。
解决方案:在过虑器中。包装httpServletRequest类,修改getParameter方法。即可
过滤器
用户进行评论时,需要把数据提交给后台Servlet进行处理,这时还没到达Servlet时就把请求拦截,并且更换成MyRequest,然后采用敏感词汇替换工具进行敏感词汇替换。
package cn.hncu.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.HttpServletRequestWrapper;
import cn.hncu.pub.WordsUtils;
public class WordsFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
//这里采用装饰模式,把HttpServletRequest改装成 MyRequest,然后用MyRequest进行方行
MyRequest myRequest = new MyRequest((HttpServletRequest) request);
chain.doFilter(myRequest, response); //放行
}
public void init(FilterConfig fConfig) throws ServletException {
}
}
class MyRequest extends HttpServletRequestWrapper{
public MyRequest(HttpServletRequest request) {
super(request);
}
//改装 getParameter()方法,按理应该把重载的函数都改装了,这里就意思一下
@Override
public String getParameter(String name) {
String src = super.getParameter(name);
//调用敏感词汇过滤工具进行过滤
String dest = WordsUtils.wordsFilter(src);
return dest;
}
}
敏感词汇替换工具
从数据库中把敏感词汇加载出来,然后把用户评论中的敏感词汇进行替换。
package cn.hncu.pub;
import java.util.ArrayList;
import java.util.List;
/**
*   处理敏感词的工具
* <br/><br/><b>CreateTime:</b><br/>    2018年9月26日 下午12:25:11
* @author 宋进宇 <a href='mailto:447441478@qq.com'>447441478@qq.com</a>
*/
public class WordsUtils {
private static List<String> list;
static {
//按理这里应该是从数据库读取出所有敏感词汇,这里就模拟了
list = new ArrayList<String>();
list.add("SB");
list.add("骂人");
list.add("大大");
}
//对外关闭构造函数
private WordsUtils() {
}
/**
* 获取敏感词汇集
* @return
*/
public static List<String> getWords(){
return list;
}
/**
* 重新加载敏感词汇库
*/
public static void rebuild() {
//按理这里应该访问service-->dao-->数据库,读取数据,这里模拟了
System.out.println("敏感词汇加载完毕.");
}
/**
* 把敏感词汇进行过滤
* @param src 原数据
* @return 过滤过的数据
*/
public static String wordsFilter(String src) {
for (String word : list) {
src = src.replaceAll(word, "<font color='gray'>[和谐]</font>");
}
return src;
}
}
完整代码链接
运用四:全站压缩
运用场景:现在的网络,流量就是钱。所以,如果能在很少的流量的情况下,查看相同的数据内容,那何乐而不为呢?
实现方案:
•用户在调用response.getOutputStream()时让获取自己的输出流对像, 我们将信息写到事先准备好的缓存当中。
•当用户书写完毕,我们再调用自己提供的方法,获取缓存中的数据流。
•然后接着对数据进行压缩,在过虑器中,将信息返回给用户,从而实现数据的压缩。
•那么如何用户调用response.getWriter进行输出时,我们怎么办呢?
•能不能也对jsp所有输出进行压缩呢?
•可以处理中文乱码问题吗?
话不多说上代码
流压缩见Web---http协议---常用的参数文章
过程图解
压缩字节流
其实就是把原来的response改成包装类,并且把response.getOutputStream()获得流对象也改成包装类,这样在Servlet中进行字节流输出时,其实源数据输出的动向都是为我们所掌控。
package cn.hncu.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.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
public class Gzip1Filter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//在这里需要把 response 换成MyResponse, response.getOutputStream 换成 MyOutputStream
//这里我们知道 response 对象其实是 HttpServletResponse类对象,所有强转以便后面操作。
HttpServletResponse resp = (HttpServletResponse) response;
MyResponse myResponse = new MyResponse(resp);
//放行参数的用myResponse
chain.doFilter(request, myResponse); //放行
//能到这里说明servlet 已经运行完 doGet/doPost 方法
//这时候可以 通过 myResponse获取封装在它内部的 字节内存流对象,把原数据获取出来进行压缩
ByteArrayOutputStream baos = myResponse.getBaos();
byte[] src = baos.toByteArray();
System.out.println( "压缩前长度:"+src.length );
//下面开始进行字节数据压缩///
//1 创建一个字节内存流,用来存放压缩后的数据
ByteArrayOutputStream destBaos = new ByteArrayOutputStream();
//2 创建 GZIPOutputStream,并且把 创建的字节内存流传过去,接收压缩后的数据
GZIPOutputStream gzipOs = new GZIPOutputStream(destBaos);
//3 把 原字节数据写入 gzipOs 进行压缩
gzipOs.write(src);
//4 写完一定要刷缓存!!!!!!!!
gzipOs.close(); //gzipOs.flush(); //调close()内部自动刷缓存,而且还可以防止内存泄漏
//5 从存储 压缩完的数据的 destBaos中获取数据写给客户端
byte[] dest = destBaos.toByteArray();
System.out.println( "压缩后长度:"+dest.length );
//6 写给客户端前 一定要设置响应头 !!! --- 注意 写给客户端时用的是:response对象
resp.setHeader("Content-Encoding", "gzip");
//7通过字节流输出给客户端.
resp.getOutputStream().write(dest);
}
@Override
public void destroy() {
}
class MyResponse extends HttpServletResponseWrapper{
//用来存储 servlet中 write时的原数据
private ByteArrayOutputStream baos;
public ByteArrayOutputStream getBaos() {
return baos;
}
public MyResponse(HttpServletResponse response) {
super(response);
baos = new ByteArrayOutputStream();
}
//把原装的ServletOutputStream的类对象 改装成MyServletOutputStream类对象//
@Override
public ServletOutputStream getOutputStream() throws IOException {
MyServletOutputStream myServletOutputStream = new MyServletOutputStream( super.getOutputStream(),baos);
return myServletOutputStream;
}
//
}
class MyServletOutputStream extends ServletOutputStream{
private ServletOutputStream sos;
//用来存储 servlet中 write时的原数据
private ByteArrayOutputStream baos;
protected MyServletOutputStream(ServletOutputStream sos,ByteArrayOutputStream baos) {
this.sos = sos;
this.baos = baos;
}
@Override
public boolean isReady() {
return sos.isReady();
}
@Override
public void setWriteListener(WriteListener arg0) {
sos.setWriteListener(arg0);
}
//把write方法修改了
@Override
public void write(int b) throws IOException {
baos.write(b);
}
///
}
}
压缩字符流
同字节类似,但实现起来更简单,因为PrintWriter类可以封装一个字节流,正好可以 '字节内存输出流' 封装进去,这样servlet调用字符输出流的时候,其实是把数据输出到 '字节内存输出流' 中去,等servlet处理完后,把数据进行压缩再输出。
package cn.hncu.filter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
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 javax.servlet.http.HttpServletResponseWrapper;
public class Gzip2Filter implements Filter{
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
//在这里需要把 response 换成MyResponse, response.getOutputStream 换成 MyOutputStream
//这里我们知道 response 对象其实是 HttpServletResponse类对象,所有强转以便后面操作。
HttpServletResponse resp = (HttpServletResponse) response;
MyResponse myResponse = new MyResponse(resp);
//放行参数的用myResponse
chain.doFilter(request, myResponse); //放行
//能到这里说明servlet 已经运行完 doGet/doPost 方法
//这时候可以 通过 myResponse获取封装在它内部的 字节内存流对象,把原数据获取出来进行压缩
ByteArrayOutputStream baos = myResponse.getBaos();
byte[] src = baos.toByteArray();
System.out.println( "压缩前长度:"+src.length );
//下面开始进行字节数据压缩///
//1 创建一个字节内存流,用来存放压缩后的数据
ByteArrayOutputStream destBaos = new ByteArrayOutputStream();
//2 创建 GZIPOutputStream,并且把 创建的字节内存流传过去,接收压缩后的数据
GZIPOutputStream gzipOs = new GZIPOutputStream(destBaos);
//3 把 原字节数据写入 gzipOs 进行压缩
gzipOs.write(src);
//4 写完一定要刷缓存!!!!!!!!
gzipOs.close(); //gzipOs.flush(); //调close()内部自动刷缓存,而且还可以防止内存泄漏
//5 从存储 压缩完的数据的 destBaos中获取数据写给客户端
byte[] dest = destBaos.toByteArray();
System.out.println( "压缩后长度:"+dest.length );
//6 写给客户端前 一定要设置响应头 !!! --- 注意 写给客户端时用的是:response对象
resp.setHeader("Content-Encoding", "gzip");
//7通过字节流输出给客户端.
resp.getOutputStream().write(dest);
}
@Override
public void destroy() {
}
class MyResponse extends HttpServletResponseWrapper{
//用来存储 servlet中 write时的原数据
private ByteArrayOutputStream baos;
private PrintWriter writer;
//有个细节 当访问的是 jsp文件时,需要把writer中的数据刷一下,因为jspWriter没有自带自动刷缓存///
public ByteArrayOutputStream getBaos() {
writer.close();
return baos;
}
public MyResponse(HttpServletResponse response) {
super(response);
baos = new ByteArrayOutputStream();
}
//把原装的PrintWriter的类对象 替换掉//
@Override
public PrintWriter getWriter() throws IOException {
//这里设置自带刷缓存只对Servlet中的字符输出流有效,对jsp的字符输出流是无法影响的。
writer = new PrintWriter( baos,true);
return writer;
}
/
}
}
全站压缩:字节、字符都可以压缩
package cn.hncu.filter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.zip.GZIPOutputStream;
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
public class GzipAllFilter implements Filter {
public void destroy() {
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
/* 在这里需要把 response 换成MyResponse,
* response.getOutputStream 换成 MyOutputStream,
* response.getWriter 换成 new PrintWirter(baos,true);
*/
//这里我们知道 response 对象其实是 HttpServletResponse类对象,所有强转以便后面操作。
HttpServletResponse resp = (HttpServletResponse) response;
MyResponse myResponse = new MyResponse(resp);
//放行参数的用myResponse
chain.doFilter(request, myResponse); //放行
//能到这里说明servlet 已经运行完 doGet/doPost 方法
//这时候可以 通过 myResponse获取封装在它内部的 字节内存流对象,把原数据获取出来进行压缩
ByteArrayOutputStream baos = myResponse.getBaos();
byte[] src = baos.toByteArray();
System.out.println( "压缩前长度:"+src.length );
//下面开始进行字节数据压缩///
//1 创建一个字节内存流,用来存放压缩后的数据
ByteArrayOutputStream destBaos = new ByteArrayOutputStream();
//2 创建 GZIPOutputStream,并且把 创建的字节内存流传过去,接收压缩后的数据
GZIPOutputStream gzipOs = new GZIPOutputStream(destBaos);
//3 把 原字节数据写入 gzipOs 进行压缩
gzipOs.write(src);
//4 写完一定要刷缓存!!!!!!!!
gzipOs.close(); //gzipOs.flush(); //调close()内部自动刷缓存,而且还可以防止内存泄漏
//5 从存储 压缩完的数据的 destBaos中获取数据写给客户端
byte[] dest = destBaos.toByteArray();
System.out.println( "压缩后长度:"+dest.length );
//6 写给客户端前 一定要设置响应头 !!! --- 注意 写给客户端时用的是:response对象
resp.setHeader("Content-Encoding", "gzip");
//7通过字节流输出给客户端.
resp.getOutputStream().write(dest);
}
public void init(FilterConfig fConfig) throws ServletException {
}
}
class MyResponse extends HttpServletResponseWrapper{
//用来存储 servlet中 write时的原数据
private ByteArrayOutputStream baos;
//将数据输出到 baos 中去的打印流
private PrintWriter writer;
public ByteArrayOutputStream getBaos() {
//两个流混用是 一定要注意:close 时要判断writer是否为空
if( writer != null ) {
writer.close();
}
return baos;
}
public MyResponse(HttpServletResponse response) {
super(response);
baos = new ByteArrayOutputStream();
}
********下面是把原有来的 getOutputStream和getWriter改掉*********
//把原装的ServletOutputStream的类对象 改装成MyServletOutputStream类对象//
@Override
public ServletOutputStream getOutputStream() throws IOException {
MyServletOutputStream myServletOutputStream = new MyServletOutputStream( super.getOutputStream(),baos);
return myServletOutputStream;
}
//
//把原装的PrintWriter的类对象 替换掉//
@Override
public PrintWriter getWriter() throws IOException {
//这里设置自带刷缓存只对Servlet中的字符输出流有效,对jsp的字符输出流是无法影响的。
writer = new PrintWriter( baos,true);
return writer;
}
/
}
class MyServletOutputStream extends ServletOutputStream{
private ServletOutputStream sos;
//用来存储 servlet中 write时的原数据
private ByteArrayOutputStream baos;
protected MyServletOutputStream(ServletOutputStream sos,ByteArrayOutputStream baos) {
this.sos = sos;
this.baos = baos;
}
@Override
public boolean isReady() {
return sos.isReady();
}
@Override
public void setWriteListener(WriteListener arg0) {
sos.setWriteListener(arg0);
}
//把write方法修改了
@Override
public void write(int b) throws IOException {
baos.write(b);
}
///
}
使用压缩流的一些细节:
配置只对部分和.jsp压缩,其他的不压缩, 还应配置对.js,.css压缩。但决不能配置对所有请求都压缩,因为如果用户请求的是下载,那不但不能压缩,反而会让服务器因内存益处而崩溃。