使用Decorator设计模式增强response对象
对于页面中很少更新的数据,例如商品分类,为避免每次都要从数据库查询分类数据,因此可把分类数据缓存在内存或文件中,以此来减轻数据库压力,提高系统响应速度。
试想一下,如果我们真要写这样一个缓存过滤器,那就要把页面中很少更新的数据给缓存起来,紧接着我们就要问自己,用什么东西给他们存起来呢?可以用Map集合嘛!如果使用Map<K, V>集合,那么K和V又分别代表什么呢?我大概想:
- K应该是String,也就是用户想访问的资源(uri),如/ServletDemo4等;
- V应该是byte[],也就是缓存数据,因为归根究底,不管是什么格式的数据,最后存在计算机里面的就是字节数组了。
我们编写这样的一个缓存过滤器,代码应该就是:
package cn.liayun.web.filter.example;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
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.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;
public class WebCacheFilter implements Filter {
/*
* Map<"/ServletDemo4", byte[]>
*
* key:资源的uri,例如.../ServletDemo4
* value:该资源的缓存数据(字节数组)
*/
private Map<String, byte[]> map = new HashMap<String, byte[]>();
@Override
public void init(FilterConfig filterConfig) throws ServletException {
// TODO Auto-generated method stub
}
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
//1. 得到用户想访问的资源(uri)
String uri = request.getRequestURI();
//2. 看Map集合中是否保存了该资源的数据
byte[] b = map.get(uri);
//3. 如果保存了,则直接取出数据打给浏览器
if (b != null) {
response.getOutputStream().write(b);
return;
}
//4. 如果没有保存资源对应的数据,则放行,让目标资源执行,这时还须写一个response的包装类,捕获目标资源的输出
MyResponse myResponse = new MyResponse(response);
chain.doFilter(request, myResponse);
byte[] data = myResponse.getBuffer();
//5. 以资源的uri为关键字,把资源对应的数据保存在Map集合中,已备于下次访问
map.put(uri, data);
//6. 输出数据给浏览器
response.getOutputStream().write(data);
}
class MyResponse extends HttpServletResponseWrapper {
//维护一个缓冲流
private ByteArrayOutputStream bout = new ByteArrayOutputStream();
private PrintWriter pw;
private HttpServletResponse response;
public MyResponse(HttpServletResponse response) {
super(response);
this.response = response;
}
@Override
public ServletOutputStream getOutputStream() throws IOException {
// myResponse.getOutputStream().write("写出数据");
return new MyServletOutputStream(bout);
}
@Override
public PrintWriter getWriter() throws IOException {
/*
* myResponse.getWriter().write("java组件多。我个人......");
* 调用我的PrintWriter对象写出数据,由于PrintWriter是一个包装流,写出的数据太小的话,它是不会写到底层流bout里面去的,
* 而是写到了PrintWriter对象的缓冲里面。
* 为了确保数据能够写到底层流bout里面去,应该怎么做呢?
*
* return new PrintWriter(bout);
*/
// return new PrintWriter(bout);
// pw = new PrintWriter(bout);
//为了解决中文乱码
pw = new PrintWriter(new OutputStreamWriter(bout, response.getCharacterEncoding()));
return pw;
}
//定义一个方法返回bout缓冲区对象里面的数据(得到底层流中的数据)
public byte[] getBuffer() {
if (pw != null) {
pw.close();//确保数据一定会写到底层流bout里面去
}
return bout.toByteArray();
}
}
class MyServletOutputStream extends ServletOutputStream {
private ByteArrayOutputStream bout = null;
public MyServletOutputStream(ByteArrayOutputStream bout) {
this.bout = bout;
}
@Override
public boolean isReady() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setWriteListener(WriteListener listener) {
// TODO Auto-generated method stub
}
@Override
public void write(int b) throws IOException {
bout.write(b);
}
}
@Override
public void destroy() {
// TODO Auto-generated method stub
}
}
紧接着我们就要在web.xml中配置Web资源缓存过滤器了。
我们只是对指定的Web资源——ServletDemo4进行过滤。ServletDemo4是一个用来显示分类的Servlet,具体代码是:
package cn.liayun.web.servlet;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//显示分类的Servlet
@WebServlet("/ServletDemo4")
public class ServletDemo4 extends HttpServlet {
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//查询数据,获取到分类信息
String data = "分类信息数据";
//输出分类信息
response.getWriter().write(data);
System.out.println("ServletDemo4执行了");
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
doGet(request, response);
}
}
当有人首次访问ServletDemo4时,确实会查询数据库,获取到分类信息,并显示出分类信息,但当他再次点击刷新按钮时,ServletDemo4就不会从数据库中查询分类数据了,因为分类数据已经缓存在Map集合中了,这样是不是减轻了数据库压力,并提高系统的响应速度了呢?