您的位置:首页 > 编程语言 > Java开发

【Java EE 学习 19】【使用过滤器实现全站压缩】【使用ThreadLocal模式解决跨DAO事务回滚问题】

2015-06-15 20:46 876 查看
[b]一、使用过滤器实现全站压缩[/b]

1.目标:对网站的所有JSP页面进行页面压缩,减少用户流量的使用。但是对图片和视频不进行压缩,因为图片和视频的压缩率很小,而且处理所需要的服务器资源很大。

2.实现原理

  (1)使用GZIPOutputStream工具对数据进行压缩,中间借助了ByteArrayOutputStream类进行结果的存储。

  (2)使用过滤器对浏览器请求进行拦截,通过自定义HttpServletResponse类(使用包装模式),重写getWriter方法,使得写出的目的地转变成ByteArrayOutputStream对象,即内存;通过查看JSP引擎解析之后的源代码,可以发现Servlet使用out对象向浏览器输出html代码,out对象是PageContext对象的一个成员变量,为JspWriter类的实例;而JspWriter实际上是对Writer类的包装,其本身和PrintWriter是兄弟关系;

  (3)JSP页面中使用的out对象和浏览器请求时产生的response对象通过方法getPrintWriter得到的对象是同一个对象。具体验证方法可以通过在自定义Response对象中的getWriter方法中进行打印输出验证。JspWriter类中有关键代码:  

if (this.out == null) {
this.out = this.response.getWriter();
}


而out对象有定义:

public class JspWriterImpl
extends JspWriter
{
private Writer out;
......
}


  (5)具体过程:过滤器拦截浏览器向JSP页面发出的请求,并将请求的response对象替换成自定义的response对象,该对象重写了getWriter方法,使得JSP页面中的代码在经过JSP解析引擎解析之后的Servlet代码写出的目的地由浏览器变成了内存(ByteArrayOutputStream);过滤器拦截服务器向浏览器发出的响应信息,并将内存中的数据拿出来(ByteArrayOutputStream.toByteArray()),使用GZIPOutputStream进行压缩(压缩到ByteArrayOutputStream),最后通过getOutputSteam方法获得原生输出流对象,将压缩之后的数据写出到浏览器。

3.问题升级

  (1)现在又需求使用Servlet,而且要对Servlet进行压缩

  (2)Servlet使用getOutputStream方法获得输出流。

  (3)问:如果只是2.(5)的操作流程能解决这个需求吗?

答:不能,将会产生空指针异常,原因是ByteArrayOutputStream在重写的getWriter中被赋初值,如果没有调用该方法,则该变量一直为NULL,几乎不能对该变量进行任何操作,否则一定会报空指针异常。

  (4)解决方法:重写getOutputStream方法,通过和getWriter相同的目的将JSP页面的代码写到ByteArrayOutputStream(实现方式不同)

  (5)疑问:这样会不会对之前的JSP页面的压缩产生影响?之前的页面调用了response的getOutputStream方法~

    答:不会。最后输出的时候使用的response是原生的response对象,该对象的getOutputStream方法写到的目的地永远都是浏览器,而重写的getOutputStream方法只在自定义的HttpServletResponse,所以二者不会相互影响。

4.代码。

package com.kdyzm.filter;

import java.io.IOException;
import java.sql.Connection;
import java.sql.SQLException;

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 com.kdyzm.dbutils.DataSourceUtils_c3p0;

public class TransactionFilter implements Filter{

@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("初始化事务过滤器!");
}

@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
System.out.println("拦截过滤:");
Connection conn=null;
try{
conn=DataSourceUtils_c3p0.getConnection();
conn.setAutoCommit(false);
chain.doFilter(request, response);
conn.commit();
}
catch(Exception e){
System.out.println("出错了!回滚!!");
try {
conn.rollback();
} catch (SQLException e1) {
e1.printStackTrace();
}
e.printStackTrace();
}
finally{
try {
conn.close();
DataSourceUtils_c3p0.remove();//这句代码十分重要,解决了两次刷新产生空指针异常、不能操作已经关闭的Connection的问题。
} catch (SQLException e) {
e.printStackTrace();
}
}
}
@Override
public void destroy() {
System.out.println("销毁事务过滤器!");
}

}


TransactionFilter.java类源代码
8.扩展

  如果数据库的操作都成功了,只是JSP页面的一个/0的错误,这样的一个错误仍然会被过滤器拦截并导致数据库回滚,该怎样避免该问题的发生?

  答:在回滚之前判断异常类型,如果是SQLException类型的则回滚,否则不回滚。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: