创建Connection的过程是非常耗时的,为了保证Conection可以重用。应该将Connection进行池管理。
使用静态工厂方法管理一个唯一的连接:
package cn.itcast.utils; import java.sql.Connection; import java.sql.Driver; import java.sql.DriverManager; import java.util.Enumeration; /** * 用静态工厂方法管理一个唯一的可重用的连接 */ public class ConnUtils { private static Connection con; //在静态代码块中创建与数据库的连接 static{ try{ Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql:///db909?characterEncoding=UTf8"; con = DriverManager.getConnection(url,"root","1234"); }catch(Exception e){ throw new RuntimeException(e.getMessage(),e); } } //使用一个静态方法-静态工厂方法,返回connection实例 public static Connection getCon(){ return con; } }
连接池的实现
为什么要有连接池?
1:维护多个连接。必须要保证每一个线程获取到的是不同的connection对象。
2:提供一个方法可以回收连接。
以下是最基本的实现,但是实际都不这么用的代码段:
package cn.itcast.utils; import java.sql.Connection; import java.sql.DriverManager; import java.util.ArrayList; import java.util.List; public class ConnUtils2 { //声明一个容器,放所有声明的连接Connection private static List<Connection> pool = new ArrayList<Connection>(); static{ try{ Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql:///db909?characterEncoding=UTf8"; for(int i=0;i<3;i++){ //创建三个连接 Connection con = DriverManager.getConnection(url,"root","1234"); //将这个三个连接放到pool中去 pool.add(con); } System.err.println("连接是:"+pool); }catch(Exception e){ throw new RuntimeException(e.getMessage(),e); } } public static Connection getCon(){ synchronized (pool) { Connection con = pool.remove(0); try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.err.println("还有:"+pool.size()); return con; } } //手工的还连接 public static void back(Connection con){ System.err.println("还连接:"+con); pool.add(con); } }
6、在代码中调用close时也可以还连接,对close方法进行修改。
动态代理,作用:
1:对某个方法增强。
2:在不污染源类的情况下,修改原类的行为。
代理类,与被代理类,两个不同的实体。
要求:所有被代理的类,都必须要拥有一个接口。本质上是对方法进行修改,但其实它是通过反射执行的某个方法。
动态代理的核心类:
Proxy – 用于创建给定接口的子类,在内存中动态的创建。$Proxy0.此类只使用一次。
InovocationHandler – 执行句柄。在执行时可以获取被代理类的所有反射。用户的每一次调用都会被这个句柄拦截到。
package cn.itcast.demo; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyDemo2 { public static void main(String[] args) throws Exception { final GDG o = new GDG(4); System.err.println("1:声明被代理的对象"+o+","+o.getClass()); Object proxyedObj = Proxy.newProxyInstance( ProxyDemo2.class.getClassLoader(), new Class[]{IGDG.class}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.err.println("5:正在调用的方法是:"+method.getName());//talk Object returnValue = method.invoke(o,args); System.err.println("6:通过反射调用目标(被代理类)类的方法成功,返回值是:"+returnValue); return "你不好"; } } ); System.err.println("2:代理类创建完成:"+proxyedObj.getClass()); System.err.println("3:强转成IGDG接口"); IGDG o2 = (IGDG) proxyedObj; System.err.println("4:调用代理类的talk方法"); String back = o2.talk(); System.err.println("7:方法都调用完成 "+back); } }
package cn.itcast.demo; public class GDG implements IGDG { int nu = 0; public GDG(int nm) { nu=nm; } public String talk(){ System.err.println("5.1:开始讲了....."+nu); return "你好:"+nu; } }
package cn.itcast.demo; public interface IGDG { String talk(); }
代理的任务
1:在内存中创建某个接口的子类。
2:拦截所有在代理上执行的方法。( 除了getClass方法。)
用动态代理书写连接池
设计:
代理的目标:原生的connection。
代理的目的:修改close方法,让close方法不可以关闭连接,且主动收回连接。
通过动态代理,和线程通讯:
1:对Connection进行代理。
2:在获取Connection时,通过同步,如果没有连接时,就让线程进入等待池。
3:修改close方法,且在还了连接以后唤醒正在等待的线程。
package cn.itcast.utils; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.DriverManager; import java.util.ArrayList; import java.util.List; public class ConnUtils4 { //第一步:声明连接池维护所有的连接 private static List<Connection> pool = new ArrayList<Connection>(); //第二步:静态代码块中创建多个连接 static{ try{ Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql:///db909?characterEncoding=UTF8"; for(int i=0;i<3;i++){ final Connection con = DriverManager.getConnection(url,"root","1234");//com.mysql.jdbc.Jdbc4Connection@ //对con对象进行动态代理生成Connectioin接口的子类,同时也是con的代理类。
Object proxyedCon = Proxy.newProxyInstance( ConnUtils4.class.getClassLoader(), new Class[]{Connection.class}, //声明执行句柄,只对close方法设置拦截 new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("close")){ System.err.println("有人想关闭连接,不能关,还连接"); //将proxy再加到pool中,这个proxy就是proxyedCon synchronized (pool) {//将代理对象,再重新加回到pool中来,这个proxy参数,就是this. pool.add((Connection) proxy); pool.notify(); } return null; }else{ System.err.println("放行"+method.getName()); return method.invoke(con, args);//通过反射执行con即被代理对象的方法。 } } }); //一定要将代理对象添加到池中去。 pool.add((Connection) proxyedCon); } }catch(Exception e){ throw new RuntimeException(e.getMessage(),e); } } /** * 提供一个静态工厂方法返回一个连接 */ public static Connection getCon(){ synchronized (pool) { if(pool.size()==0){ try { pool.wait(); } catch (InterruptedException e) { e.printStackTrace(); } return getCon(); } Connection con = pool.remove(0);//返回一个代理的connection对象 System.err.println("还有几个:"+pool.size()); return con; } } }
优化版:
通过类加载器读取一个资源文件:
SomeClass.class.getResource(xxx) – 获取与SomeClass字节码同一个目录下的xxx文件。
SomeClass.class.getClassLoader().getResource(“xxxx”); - 获取classpath根下上的xxx文件。
1:将url,driver,name,pwd写到一个配置文件中去。-properties
2:通过LinkedList来维护一个池。
package cn.itcast.utils; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLDecoder; import java.sql.Connection; import java.sql.DriverManager; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Properties; public class ConnUtils3 { private static LinkedList<Connection> pool = new LinkedList<Connection>(); static{ try { //声明资源器类 - Properties prop = new Properties(); //获取这个文件的路径 URL url = ConnUtils3.class.getClassLoader().getResource("jdbc.properties"); String path = url.getPath(); //为了防止有中文或是空格 path = URLDecoder.decode(path,"UTf-8"); File file = new File(path); //加载jdbc.properties这个文件 prop.load(new FileInputStream(file)); //获取信息 String driver = prop.getProperty("driver"); Class.forName(driver); String jdbcurl = prop.getProperty("url"); String nm = prop.getProperty("name"); String pwd = prop.getProperty("pwd"); //创建三个原生的连接,都将它们代理 String poolSize = prop.getProperty("poolSize"); int size = Integer.parseInt(poolSize); for(int i=0;i<size;i++){ final Connection con = DriverManager.getConnection(jdbcurl,nm,pwd); //对con进行动态代理 Object proxyedObj = Proxy.newProxyInstance(ConnUtils3.class.getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //是否是close方法 if(method.getName().equals("close")){ synchronized (pool) { pool.addLast((Connection) proxy); pool.notify(); } return null;//如果调用的是close则不会调用被代理类的方法。 } return method.invoke(con, args); } }); //将代理对象放到pool中 pool.add((Connection) proxyedObj); } } catch (Exception e) { e.printStackTrace(); } } public static Connection getConn(){ synchronized (pool) { if(pool.size()==0){ try { pool.wait(); } catch (InterruptedException e) { e.printStackTrace(); } return getConn(); }else{ Connection con = pool.removeFirst(); System.err.println("还有几个:"+pool.size()); return con; } } } }
再说连接池的作用
1:维护多个连接。
在初始化时创建List,将多个连接放到List中去.
2:可以在close时回收连接
对Connection进行动态代理,
对close方法进行拦截。
标准连接池的实现
1:实现dataSource接口。
2:声明一个集合类用于管理多个连接。
3:必须要拥有一种能力,回收连接。
4:必须要实现一个方法,getConnection以获取一个连接。
5:实现DataSource接口的类,一般不能拥有static池对象。List.
6:在一个程序中,要求只拥有一个DataSource实例就可以了。
简单实现:
// 声明一个连接池 private LinkedList<Connection> pool = new LinkedList<Connection>(); // 在初始化这个DataSourc的子类时在构造方法设置多个连接 public DBpool() { try { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://127.0.0.1:3306/contacts?useUnicode=true&characterEncoding=UTF8"; for (int i = 0; i < 3; i++) { Connection con = DriverManager.getConnection(url,"root","admin"); pool.add(con); } } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } @Override public Connection getConnection() throws SQLException { return pool.removeFirst(); }
通过代理进行改造:
package javaee.datasource; import java.io.PrintWriter; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import java.sql.SQLFeatureNotSupportedException; import java.util.LinkedList; import java.util.logging.Logger; import javax.sql.DataSource; public class DBpool implements DataSource { // 声明一个连接池 private LinkedList<Connection> pool = new LinkedList<Connection>(); // 在初始化这个DataSourc的子类时在构造方法设置多个连接 public DBpool() { try { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://127.0.0.1:3306/contacts?useUnicode=true&characterEncoding=UTF8"; for (int i = 0; i < 3; i++) { final Connection con = DriverManager.getConnection(url, "root", "admin"); Object proxyedConn = Proxy.newProxyInstance( DBpool.class.getClassLoader(), new Class[] { Connection.class }, new InvocationHandler() { @Override public Object invoke(Object proxyedConnection, Method method, Object[] args) throws Throwable { if (method.getName().equals("close")) { synchronized (pool) { pool.addLast((Connection) proxyedConnection); pool.notify(); } return null; } // 目标返回值 Object returnValue = method.invoke(con, args); return returnValue; } }); pool.add((Connection) proxyedConn); } } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } @Override public Connection getConnection() throws SQLException { synchronized (pool) { if (pool.size() == 0) { try { pool.wait(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } return getConnection(); } } Connection con = pool.removeFirst(); System.err.println("size:" + pool.size()); return con; } //... }
要求你自己实现一个类MyInputStream读取文件,且不能抛出异常(认识包装)
package javaee.wrapper; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; public class TestDemo { public static void main(String[] args) throws Exception { InputStream is=new MyinputStream("d:/a.txt"); byte[] bt=new byte[1024]; int len=0; while((len=is.read(bt))!=-1){ String s=new String(bt,0,len); System.err.println(s); } is.close(); } } package javaee.wrapper; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; class MyinputStream extends InputStream { private InputStream is; public MyinputStream(String path) { try { is = new FileInputStream(path); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public int read(byte[] b) { int i = -1; try { i = is.read(b); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return i; } public void close() { try { is.close(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } @Override public int read() throws IOException { // TODO Auto-generated method stub return 0; } }
以下通过包装实现对close方法的修改,以回收连接
1:实现Connection接口,拥有一个Connection的成员。
2:修改close方法。
3:其他的方法都调用成员变量的connection。
实现的部分代码:
public class DBpool_wrapper implements DataSource { // 声明一个连接池 private LinkedList<Connection> pool = new LinkedList<Connection>(); // 在初始化这个DataSourc的子类时在构造方法设置多个连接 public DBpool_wrapper() { try { Class.forName("com.mysql.jdbc.Driver"); String url = "jdbc:mysql://127.0.0.1:3306/contacts?useUnicode=true&characterEncoding=UTF8"; for (int i = 0; i < 3; i++) { Connection con = DriverManager.getConnection(url, "root", "admin"); MyConn conn = new MyConn(con); pool.add(conn); } } catch (Exception e) { throw new RuntimeException(e.getMessage(), e); } } public Connection getConnection() { synchronized (pool) { if (pool.size() == 0) { try { pool.wait(); } catch (InterruptedException e) { e.printStackTrace(); } return getConnection(); } Connection con = pool.removeFirst(); System.err.println("size:" + pool.size()); return con; } } public class MyConn implements Connection { private Connection conn; // 通过构造接收MySql的connection的对象JDBC4Connection@8888 public MyConn(Connection con) { this.conn = con; } public void close() throws SQLException { synchronized (pool) { System.err.println("有人还连接:" + this); pool.add(this); pool.notify(); } } } }
用包装处理get方式的乱码:
package javaee.utils; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.lang.reflect.Method; import java.util.Iterator; import java.util.Map; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import org.apache.taglibs.standard.lang.jstl.ValueSuffix; public abstract class BaseServlet extends HttpServlet { @Override protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { doPost(req, resp); } @Override protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { req.setCharacterEncoding("UTF-8"); // getparamters String methodName = req.getParameter("cmd"); // 通过反射调用子类的方法 try { Method method = this.getClass().getMethod(methodName, HttpServletRequest.class, HttpServletResponse.class); MyServletRequest myreq = new MyServletRequest(req); method.invoke(this, myreq, resp); } catch (Exception e) { // TODO Auto-generated catch block throw new RuntimeException(e.getMessage(), e); } } } class MyServletRequest extends HttpServletRequestWrapper { HttpServletRequest req; public MyServletRequest(HttpServletRequest request) { super(request); this.req = request; } public String getParameter(String name) { // TODO Auto-generated method stub String value = req.getParameter(name); if (req.getMethod().equals("GET")) { System.err.println("Decode"); try { value = new String(value.getBytes("ISO-8859-1"), "UTF-8"); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return value; } public String[] getParameterValues(String name) { // TODO Auto-generated method stub String[] value = req.getParameterValues(name); if (req.getMethod().equals("GET")) { System.err.println("Decode[]"); try { for (int i = 0; i < value.length; i++) { value[i] = new String(value[i].getBytes("ISO-8859-1"), req.getCharacterEncoding()); } } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return value; } public Map<String, String[]> getParameterMap() { Map<String, String[]> map = req.getParameterMap(); if (req.getMethod().equals("GET")) { System.err.println("Decode_Map"); Iterator<String[]> it = map.values().iterator(); while (it.hasNext()) { String[] ss = it.next(); for (int i = 0; i < ss.length; i++) { try { ss[i] = new String(ss[i].getBytes("ISO-8859-1"), req.getCharacterEncoding()); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } } return map; } }
7、小总结
1:代理或是包装都是对某个类的方法进行增强。
代理:必须要根据给定的接口,在内存中创建这个接口的子类。$Proxy0。
包装:不需要接口,但声明声明一个类,变成被包装类的子类,同时拥有一个被包装类的成员。
2:代理基本代码:
Object proxyedObj = Proxy.newProxyInstance(ClassLoader, class[]{被代理的类的接口数组.class}, new InvocationHandler(){//执行句柄 public Object invode(Object 代理,Method 方法反射,object[] args){ Reutrn method.invode(被代理类,args); } }
3:包装:
如果一个类是某个类的包装类,则:
A extends B{
Privet B b;
}
4:什么情况下,使用包装,什么情况下使用代理
如果官方(SUN)提供了包装类适配器,则应该优先使用包装。如HttpServletRequest,它的包装类就是HtpServletRequestWraper.
如果官方没有提供包装类的适配器,则可以使用动态代理。如Connection。
5:我们已经学习过的类中哪些是包装模式:
GenericServlet- 包装模式。
public abstract class GenericServlet
implements Servlet, ServletConfig, java.io.Serializable
private transient ServletConfig config;
IO
New BufferedReader(New FileReader(new FileInpjutStreamRreader(new FileInputSteam()));