【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类中有关键代码:
而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.代码。
TransactionFilter.java类源代码
8.扩展
如果数据库的操作都成功了,只是JSP页面的一个/0的错误,这样的一个错误仍然会被过滤器拦截并导致数据库回滚,该怎样避免该问题的发生?
答:在回滚之前判断异常类型,如果是SQLException类型的则回滚,否则不回滚。
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类型的则回滚,否则不回滚。
相关文章推荐
- Spring_讲解
- Java web分层设计
- Excel Sheet Column Number
- Java设计模式 单例设计模式
- Java多线程
- spring事务源码分析结合mybatis源码(二)
- JDK动态代理、责任链在mybatis中的应用
- Java for LeetCode 226 Invert Binary Tree
- JAVA中FutureTask
- JAVA 时间
- Java for LeetCode 224 Basic Calculator
- HTML转PDF(java版,经过测试好用,可以先用简单的html调试)
- java之String对象的比较
- Java正则表达式应用总结
- Java产生随机数
- Java集合类型(一)
- SpringMVC_The resource identified by this request is only capable of generating responses with characteristics
- spring xml解析异常
- ArrayList中元素判定相等方法
- Caused by: java.sql.SQLException: ResultSet is from UPDATE. No Data.