测试类:
package cn.hncu.pool2; import java.sql.Connection; import java.sql.SQLException; import java.sql.Statement; public class Testpool { public static void main(String[] args) { Connection con = null; try{ con = ConnsUtil.getConnection(); con.setAutoCommit(false); Statement st = con.createStatement(); String sql ="insert into stud values('P203','关羽',30) "; st.execute(sql); sql ="insert into stud values('P204','张飞',25) "; st.execute(sql); new OneThread(1).start(); new OneThread(2).start(); new OneThread(3).start(); new OneThread(4).start(); new OneThread(5).start(); System.out.println("主线程准备提交..."); con.commit(); System.out.println("主线程提交完毕..."); }catch (Exception e) { try { con.rollback(); System.out.println("主线程回滚了..."); } catch (SQLException e1) { throw new RuntimeException("主线程事务回滚失败!", e1); } }finally{ try { if(con!=null){ con.setAutoCommit(true); con.close();//这样,我们直接调用close方法就可以了!!! } } catch (SQLException e) { throw new RuntimeException("主线程连接关闭失败!", e); } } } } class OneThread extends Thread{ private int n; public OneThread(int n) { this.n = n; } @Override public void run() { Connection con = null; try{ con = ConnsUtil.getConnection(); con.setAutoCommit(false); Statement st = con.createStatement(); String sql ="insert into stud values('P40"+n+"','刘备"+n+"',30) "; st.execute(sql); sql ="insert into stud values('P41"+n+"','曹操"+n+"',25) "; st.execute(sql); System.out.println("第"+n+"个线程准备提交..."); con.commit(); System.out.println("第"+n+"个线程提交完毕..."); }catch (Exception e) { try { con.rollback(); System.out.println("第"+n+"个线程回滚了..."); } catch (SQLException e1) { throw new RuntimeException("第"+n+"事务回滚失败!", e1); } }finally{ try { if(con!=null){ con.setAutoCommit(true); con.close();//这样,我们直接调用close方法就可以了!!! } } catch (SQLException e) { throw new RuntimeException("第"+n+"连接关闭失败!", e); } } } }
测试结果:
代理模式:
代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问。
好处就是:
在某些情况下,如果不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用。
可以为原来的方法增加或者修改动作!
代理模式一般需要3个角色:
抽象角色:声明真实对象和代理对象的共同接口(必须是接口;
代理角色:代理对象角色内部含有对真实对象的引用,从而可以操作真实对象,同时代理对象提供与真实对象相同的接口以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装与包装。
真实角色(被代理的角色):代理角色所代表的真实对象,是我们最终要引用的对象。
代理是一种常用的设计模式,其目的就是为其他对象提供一个代理以控制对某个对象的访问。 代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后 续处理。
接口:
package cn.hncu.proxy.demo1; public interface IRenter { public void rent(int i); public String say(); }
实际对象:
package cn.hncu.proxy.demo1; public class Renter implements IRenter{ @Override public void rent(int i) { System.out.println("给你"+i+"个房间,需要交500元!"); } public String say() { System.out.println("Renter:你好,我是房东,房子实际只要300元"); return "房东的返回结果..."; } }
代理模式的核心部分:
package cn.hncu.proxy.demo1; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class Client { public static void main(String[] args) { //被代理的对象 final IRenter r = new Renter(); //这个obj是代理后的对象 Object obj = Proxy.newProxyInstance(Client.class.getClassLoader(), new Class[]{IRenter.class}, new InvocationHandler() { @Override //proxy是代理后的对象(等价于返回的obj),method就是类反射中的方法对象, args是执行method方法所需的参数 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("我是中介..."); //在这里,我们把房东说的话和返回值给拦截了。 if(method.getName().equals("say")){ System.out.println("我是中介,你还没交中介费,无法联系房东..."); return ""; } Object obj = method.invoke(r, args); System.out.println("欢迎再来找我中介..."); //r就是原来的对象 return obj; //这个返回结果是调用r中的方法method.invoke(r, args);返回的结果 } }); IRenter ir = (IRenter) obj; ir.rent(3); System.out.println(ir.say()); } }
可以看出来InvocationHandler这个接口中的invoke方法是最核心的部分!!!
在里面我们利用类加载器和类反射,直接可以来调用原来的类,而且可以加上自己需要的功能,或者实现拦截!!!
上面的例子只能实现对Renter 类的代理。
通用的代理模板类:
public interface IAnimal { public void run(); } public class Dog implements IAnimal{ private String name; public Dog(String name) { this.name = name; } @Override public void run() { System.out.println("Doge:"+name+"is running..."); } }
public interface IPerson { public void sayHi(); } public class Person implements IPerson { private String name; public Person(String name){ this.name = name; } @Override public void sayHi() { System.out.println(name+"说:祝你快乐..."); } }
ProxyUtil.java
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyUtil implements InvocationHandler{ private Object srcObj=null; public ProxyUtil(Object srcObj) { this.srcObj=srcObj; } public static Object getProxy(Object srcObj){ Object obj = Proxy.newProxyInstance( ProxyUtil.class.getClassLoader(), srcObj.getClass().getInterfaces(), new ProxyUtil(srcObj)); //obj是代理后的返回结果其实就是method.invoke(srcObj, args)的返回结果 return obj; } //这个就是代理的方法了 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("进入了代理区域~~"); //这个srcObj就是原来的对象 return method.invoke(srcObj, args); } }
client
package cn.hncu.proxy.demo2; import cn.hncu.proxy.demo2.domain.Dog; import cn.hncu.proxy.demo2.domain.IAnimal; import cn.hncu.proxy.demo2.domain.IPerson; import cn.hncu.proxy.demo2.domain.Person; public class Client { public static void main(String[] args) { Dog dag = new Dog("旺财"); IAnimal iDag = (IAnimal) ProxyUtil.getProxy(dag); iDag.run(); Person p = new Person("小明"); IPerson ip =(IPerson) ProxyUtil.getProxy(p); ip.sayHi(); } }
输出结果:
我们现在再来把之前用装饰模式写的ConnsUtil类改写成代理模式:
ConnsUtil类的代理模式:
package cn.hncu.pool3; 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; import java.util.Properties; public class ConnsUtil { private static List<Connection> pool = new ArrayList<Connection>(); private static final int NUM=3; static{ try { //读取配置文件 Properties p = new Properties(); p.load(ConnsUtil.class.getClassLoader().getResourceAsStream("jdbc.properties")); String driver = p.getProperty("driver"); String url = p.getProperty("url"); String user = p.getProperty("username"); String password = p.getProperty("password"); Class.forName(driver); for(int i=0;i<NUM;i++){ final Connection conn = DriverManager.getConnection(url, user, password); //只需要改这里就行了! //使用动态代理代理conn对象,实现对close方法的拦截 Object obj = Proxy.newProxyInstance( ConnsUtil.class.getClassLoader(), conn.getClass().getInterfaces(), new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equalsIgnoreCase("close") && (args==null || args.length==0)){ pool.add((Connection)proxy); return null; }else{ return method.invoke(conn, args); } } }); pool.add((Connection)obj); } } catch (Exception e) { e.printStackTrace(); } } public static synchronized Connection getConn() throws Exception{ if(pool.size()<=0){ Thread.sleep(100); return getConn(); } return pool.remove(0); } }
大家可以自己动手写一写,加深印象, Proxy.newProxyInstance(类加载器,class数组(被代理对象的接口的class数组),new InvocationHandler());
记住这个就可以了!
被代理对象的接口的class数组可以用conn.getClass().getInterfaces(),来获得。
conn为被代理的对象!
动态代理比装饰模式方便很多!