数据的压缩
GzipOutputStream - > > ByteArrayOutputStream。
以下是在某个servlet中对指定的数据进行压缩
package cn.itcast.servlet; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.OutputStream; import java.io.PrintWriter; import java.io.StringReader; import java.util.zip.GZIPOutputStream; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class GzipServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse resp) throws ServletException, IOException { //声明准备被压缩的数据 String str = "Hello你好Hello你好在内存中声明一Hello你好在" + "内存中声明一个Hello你好在内存中声明一个Hello你" + "好在内存中声明一个<br/>容器声明准备被压缩获取准备被压缩" + "的数据的字节码的数据容器声明准备被压缩获取准备被压缩的数" + "据的字节码的数据容器声明准备被压缩获取准备被压缩的数据的" + "字节码的数据个容器声明准备被压缩获取准备被压缩的数据的字节码的" + "数据在内存中声明一个容器声明准备被压缩获取准备被压缩的数据" + "的字节码的数据"; //2:获取准备被压缩的数据的字节码 byte[] src = str.getBytes("UTF-8"); //3:在内存中声明一个容器 ByteArrayOutputStream destByte = new ByteArrayOutputStream(); //4:声明压缩的工具流,并设置压缩的目的地为destByte GZIPOutputStream zip = new GZIPOutputStream(destByte); //5:写入数据 zip.write(src); //6:关闭压缩工具流 zip.close(); System.err.println("压缩之前字节码大小:"+src.length); //7:获取压缩以后数据 byte[] dest = destByte.toByteArray(); System.err.println("压缩以后的字节码大小:"+dest.length); //8:必须要输出压缩以后字节数组 resp.setContentType("text/html;charset=UTF-8"); //9:必须要使用字节流来输出信息 OutputStream out = resp.getOutputStream(); //10:通知浏览器。这是压缩的数据,要求浏览器解压 resp.setHeader("Content-encoding","gzip"); //11:通知浏览器压缩数据的长度 resp.setContentLength(dest.length); //10:输出 out.write(dest); } }
所有页面*.jsp全部压缩
只要是输出信息,只有两种方式:
Response.getWriter()..输出信息――字符流。所有的jsp页面,编译后,都是通过JspWriter方式输出的信息。但所有jsp页面都是JspWriter,而jspwriter是对PrintWriter的包装。
Response.getOutputStream() ――字节流。
分析:如果要实现全站的压缩,请先实现对所有servlet中的resp.getWriter输出的数据都压缩先实现对一个进行压缩。
第一步:书写一个类Servlet类。正常输出信息
public class OneServlet extends HttpServlet { public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html;charset=UTF-8"); PrintWriter out = response.getWriter(); out.println("Hello你好大家同学"); } }
第二步:对这上面的这个类进行拦截-Filter,在拦截过程中包装response
实现抽像类:HttpServletResponseWrapper
package cn.itcast.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.HttpServletResponse; import javax.servlet.http.HttpServletResponseWrapper; public class GzipFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //声明MyResponse包装类 MyResponse resp = new MyResponse((HttpServletResponse) response); //放行时,必须要传递自己包装过的类 chain.doFilter(request, resp); } public void destroy() { } } //实现对HttpSerlvetResponse的包装 class MyResponse extends HttpServletResponseWrapper{ public MyResponse(HttpServletResponse response) { super(response); } }
第三步:为了可以压缩数据,我们必须要拦截getwriter方法
返回的不是apache的Wrtiter对象。。
package cn.itcast.filter; public class GzipFilter implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //声明MyResponse包装类 MyResponse resp = new MyResponse((HttpServletResponse) response); //放行时,必须要传递自己包装过的类 chain.doFilter(request, resp);//第二步:这一段实现将数据写到a.txt中去。 //目标类调用完成了,返回 以后读取a.txt File file = new File("d:/a/a.txt");//第三步:读取a.txt文件 //声明一个缓存的字符串对象 StringBuilder sb = new StringBuilder(); //读取文件 InputStream in = new FileInputStream(file); byte[] b = new byte[1024]; int len = 0; while((len=in.read(b))!=-1){ sb.append(new String(b,0,len,"UTF-8")); } in.close(); //转成字节开始压缩 byte[] src = sb.toString().getBytes("UTF-8");//第四步:压缩数据 //声明缓存容器 ByteArrayOutputStream destBytes = new ByteArrayOutputStream(); //声明压缩流 GZIPOutputStream gzip = new GZIPOutputStream(destBytes); //压缩数据 gzip.write(src); gzip.close(); //获取压缩以后数据 byte[] dest = destBytes.toByteArray(); System.err.println("压缩之前:"+src.length); System.err.println("压缩以后:"+dest.length); //输出 //必须要使用原生 的response HttpServletResponse res = (HttpServletResponse) response; res.setContentType("text/html;charset=UTf-8"); OutputStream out = res.getOutputStream(); res.setHeader("Content-encoding", "gzip");//必须 res.setContentLength(dest.length); out.write(dest); } public void destroy() { } } //实现对HttpSerlvetResponse的包装 class MyResponse extends HttpServletResponseWrapper{//第一步:声明包装类 public MyResponse(HttpServletResponse response) { super(response); } @Override public PrintWriter getWriter() throws IOException { System.err.println("有人想获取输出流"); PrintWriter out = new PrintWriter( new OutputStreamWriter( new FileOutputStream("d:/a/a.txt"),"UTF-8")); return out; } }
第四步:修改包装类Myresponse2,让输出数据放到一个内存缓存区中
package cn.itcast.filter; public class GzipFilter2 implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse resp= (HttpServletResponse) response; //声明包装类对象 MyResponse2 myresp = new MyResponse2(resp); //放行,调用oneServlet.doGet chain.doFilter(request, myresp); //第二步:从myresp2中读取原生的数据 byte[] src = myresp.getSrc(); //第三步:开始压缩 ByteArrayOutputStream destBytes = new ByteArrayOutputStream(); GZIPOutputStream zip = new GZIPOutputStream(destBytes); zip.write(src); zip.close(); //第三步:输出-使用原生的response resp.setContentType("text/html;charset=UTF-8"); //获压缩以后数据 byte[] dest = destBytes.toByteArray(); System.err.println("压缩之前:"+src.length); System.err.println("压缩以后:"+dest.length); //设置头 resp.setContentLength(dest.length); resp.setHeader("Content-Encoding","gzip"); //输出 OutputStream out = resp.getOutputStream(); out.write(dest); } public void destroy() { } } //第一步:声明response的包装类 class MyResponse2 extends HttpServletResponseWrapper{ //将这个容器/a.txt,声明成员变量 private ByteArrayOutputStream srcByte; public MyResponse2(HttpServletResponse response) { super(response); } //修改增强getWtier方法 @Override public PrintWriter getWriter() throws IOException { srcByte = new ByteArrayOutputStream(); PrintWriter out = new PrintWriter( new OutputStreamWriter(srcByte, "UTF-8")); return out; } //提供一个方法获取原生 的数据 public byte[] getSrc(){ return srcByte.toByteArray(); } }
第五步:全部的jsp都要经过压缩
只要是通过包装rersponse,且修改了getWriter方法,返回一个自己的printwiter对象。声明一个放原数据的容器对象。就可以实现数据压缩。
public class GzipFilter2 implements Filter { public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { HttpServletResponse resp= (HttpServletResponse) response; //声明包装类对象 MyResponse2 myresp = new MyResponse2(resp); //放行,调用oneServlet.doGet chain.doFilter(request, myresp); //第二步:从myresp2中读取原生的数据 byte[] src = myresp.getSrc(); //第三步:开始压缩 ByteArrayOutputStream destBytes = new ByteArrayOutputStream(); GZIPOutputStream zip = new GZIPOutputStream(destBytes); zip.write(src); zip.close(); //第三步:输出-使用原生的response resp.setContentType("text/html;charset=UTF-8"); //获压缩以后数据 byte[] dest = destBytes.toByteArray(); System.err.println("压缩之前:"+src.length); System.err.println("压缩以后:"+dest.length); //设置头 resp.setContentLength(dest.length); resp.setHeader("Content-Encoding","gzip"); //输出 OutputStream out = resp.getOutputStream(); out.write(dest); } public void destroy() { } } //第一步:声明response的包装类 class MyResponse2 extends HttpServletResponseWrapper{ //将这个容器/a.txt,声明成员变量 private ByteArrayOutputStream srcByte; private PrintWriter out; public MyResponse2(HttpServletResponse response) { super(response); } //修改增强getWtier方法 @Override public PrintWriter getWriter() throws IOException { srcByte = new ByteArrayOutputStream(); out = new PrintWriter( new OutputStreamWriter(srcByte, "UTF-8")); return out; } //提供一个方法获取原生 的数据 public byte[] getSrc(){ if(out!=null){ out.close(); } return srcByte.toByteArray(); } }
配置如下:
<filter> <filter-name>zip2</filter-name> <filter-class>cn.itcast.filter.GzipFilter2</filter-class> </filter> <filter-mapping> <filter-name>zip2</filter-name> <url-pattern>*.jsp</url-pattern> </filter-mapping>
同时实现对getoutputstream和getWriter压缩
在myrespons2这个类中,对getoutputstream也要覆盖。返回一个ServletOutputSteam的匿名对象。实现writer(int b)方法,将数据写到bytearrayoutputstream.
@Override public ServletOutputStream getOutputStream() throws IOException { srcByte = new ByteArrayOutputStream(); ServletOutputStream out = new ServletOutputStream() { //所有IO最终都是一个个字节写出信息 @Override public void write(int b) throws IOException { System.err.println(">>>:"+b); srcByte.write(b);//写到自己的缓存中去-相当于StringBuffer.append(""+b); } }; return out; }
、用ThreadLocal管理事务
用三层模式:
Serlvet(MVC-C) – Sevice(服务层) – dao(数据访问层)
写两个dao,在service中调用这两个dao。
让第一个dao成功。让第二个dao失败,必须都回滚。
第一步:开发两个dao
public class UserDao2 { public void save(){ String sql = "insert into users values(?,?,?)"; QueryRunner run = new QueryRunner(); try { run.update(DataSourceUtils.getConn(),sql,"U002","Jack","333"); } catch (SQLException e) { throw new RuntimeException(e); } } }
第二步:开发Service
public class UserService { //声明两个dao private UserDao1 dao1 = new UserDao1(); private UserDao2 dao2 = new UserDao2(); public void save(){ dao1.save(); dao2.save(); } }
第三步:实现一个Servlet
public class UserServlet extends HttpServlet { //声明service的实例 private UserService service = new UserService(); public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { service.save(); } }
第四步:修改datasourceutils.java
package cn.itcast.utils; import java.sql.Connection; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class DataSourceUtils { // 声明线程局部的容器 private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); private static DataSource ds; static { ds = // 默认的读取c3p0-config.xml中默认配置 new ComboPooledDataSource("itcast"); } public static DataSource getDatasSource() { return ds; } public static Connection getConn() { // 先从tl这个容器中获取一次数据,如果当前线程已经保存过connection则直接返回这个connecton Connection con = tl.get(); if (con == null) { try { con = ds.getConnection();// 每一次从ds中获取一个新的连接 //将这个con放到tl中 tl.set(con); } catch (Exception e) { e.printStackTrace(); } } return con; } }
第五步:声明一个过虑器在过虑器开始事务
package cn.itcast.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 cn.itcast.utils.DataSourceUtils; public class TxFilter implements Filter{ public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //获取连接 Connection con = null; //在try中开始事务 try{ con = DataSourceUtils.getConn(); //开始事务 con.setAutoCommit(false); //放行 chain.doFilter(request, response); //如果没有出错。 con.commit(); }catch(Exception e){ System.err.println("出错了"); try { con.rollback(); } catch (SQLException e1) { e1.printStackTrace(); } throw new RuntimeException(e); }finally{ try { con.close(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void destroy() { } }
第六步:将过虑器配置到weeb.xml中。且对某个路径设置过虑
<filter> <filter-name>tx</filter-name> <filter-class>cn.itcast.filter.TxFilter</filter-class> </filter> <filter-mapping> <filter-name>tx</filter-name> <url-pattern>/tx/*</url-pattern> </filter-mapping>
第七步:总结
在过虑器开始事务,就叫一种模式:OSIV模式》
OSIV – Open Session In View =- 打开与数据库的会话在View层。- Hibernate.—AOP
好处:长事务。可以在View层即找开事务。
不好处:当页面上有错误时也会回滚。解决:判断异常的类型。
第八步:优化:datasourceutls.java实现一个删除thredlocal中与线程相关的对象:
package cn.itcast.utils; import java.sql.Connection; import javax.sql.DataSource; import com.mchange.v2.c3p0.ComboPooledDataSource; public class DataSourceUtils { // 声明线程局部的容器 private static ThreadLocal<Connection> tl = new ThreadLocal<Connection>(); private static DataSource ds; static { ds = // 默认的读取c3p0-config.xml中默认配置 new ComboPooledDataSource("itcast"); } public static DataSource getDatasSource() { return ds; } public static Connection getConn() { // 先从tl这个容器中获取一次数据,如果当前线程已经保存过connection则直接返回这个connecton Connection con = tl.get(); if (con == null) { try { con = ds.getConnection();// 每一次从ds中获取一个新的连接 //将这个con放到tl中 tl.set(con); } catch (Exception e) { e.printStackTrace(); } } return con; } public static void remove(){ tl.remove(); } }
在TxFilter中调用一个remove:
public class TxFilter implements Filter{ public void init(FilterConfig filterConfig) throws ServletException { } public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { System.err.println("thread:"+Thread.currentThread().getName()); //获取连接 Connection con = null; //在try中开始事务 try{ con = DataSourceUtils.getConn(); //开始事务 con.setAutoCommit(false); //放行 chain.doFilter(request, response); //如果没有出错。 con.commit(); }catch(Exception e){ System.err.println("出错了"); try { if(e instanceof SQLException){ con.rollback(); }else{ con.commit(); } } catch (SQLException e1) { e1.printStackTrace(); } throw new RuntimeException(e); }finally{ try { con.close(); DataSourceUtils.remove(); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public void destroy() { } }
在Java代码中调用Procedure
package cn.itcast.demo; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.ResultSet; import java.sql.Types; import org.junit.Test; import cn.itcast.utils.DataSourceUtils; public class ProceDemo { @Test public void proc1() throws Exception{ //dbutils不提供调用存储过程的能力 Connection con = DataSourceUtils.getDatasSource().getConnection(); //获取调用过程的对象 CallableStatement cs = con.prepareCall("{call proc1()}"); //执行 boolean boo = cs.execute();//如果返回true,指最后一句执行的是select语句 if(boo){ ResultSet rs = cs.getResultSet(); while(rs.next()){ System.err.println(rs.getString("name")); } } con.close(); } @Test public void proc2() throws Exception{ Connection con = DataSourceUtils.getDatasSource().getConnection(); //获取调用过程的对象 CallableStatement cs = con.prepareCall("{call proc2(?,?)}"); cs.setString(1,"UAAA"); cs.setString(2, "王健"); boolean boo = cs.execute(); System.err.println(boo); con.close(); } @Test public void proc3() throws Exception{ Connection con = DataSourceUtils.getDatasSource().getConnection(); //获取调用过程的对象 CallableStatement cs = con.prepareCall("{call proc5(?,?,?)}"); cs.setString(1,"UBDDB"); cs.setString(2, "张三"); cs.registerOutParameter(3,Types.INTEGER);//--int, boolean boo = cs.execute(); System.err.println(">>:"+boo);//true //从call中获取返回的值 int size = cs.getInt(3); System.err.println("行数:"+size); if(boo){ ResultSet rs= cs.getResultSet(); rs.next(); int ss = rs.getInt(1); System.err.println("sss:"+ss); } con.close(); } @Test public void proc6() throws Exception{ Connection con = DataSourceUtils.getDatasSource().getConnection(); //获取调用过程的对象 CallableStatement cs = con.prepareCall("{call proc6(?,?,?,?)}"); cs.setString(1,"UBafadsB"); cs.setString(2, "张三"); cs.registerOutParameter(3,Types.INTEGER);//--int, cs.registerOutParameter(4, Types.INTEGER); boolean boo = cs.execute(); System.err.println(">>:"+boo);//faluse //从call中获取返回的值 int size = cs.getInt(3); int _s = cs.getInt(4); System.err.println("行数:"+size+","+_s); con.close(); } }