前言:以下总结来自龙哥---左潇龙博客。
总结的很到位,附上博客链接:http://www.cnblogs.com/zuoxiaolong/p/pattern3.html
首先代理模式,可以分为两种,一种是静态代理,一种是动态代理。
两种代理从虚拟机加载类的角度来讲,本质上都是一样的,都是在原有类的行为基础上,加入一些多出的行为,甚至完全替换原有的行为。
静态代理采用的方式就是我们手动的将这些行为换进去,然后让编译器帮我们编译,同时也就将字节码在原有类的基础上加入一些其他的东西或者替换原有的东西,产生一个新的与原有类接口相同却行为不同的类型
/**
* 我们都知道,数据库连接是很珍贵的资源,频繁的开关数据库连接是非常浪费服务器的CPU资源以及内存的,
* 所以我们一般都是使用数据库连接池来解决这一问题,即创造一堆等待被使用的连接,等到用的时候就从池里取一个,不用了再放回去,数据库连接在整个应用启动期间,几乎是不关闭的,除非是超过了最大闲置时间。
*
* 但是在程序员编写程序的时候,会经常使用connection.close()这样的方法,去关闭数据库连接,而且这样做是对的,
* 所以你并不能告诉程序员们说,你们使用连接都不要关了,去调用一个其他的类似归还给连接池的方法吧。这是不符合程序员的编程思维的,也很勉强,而且具有风险性,因为程序员会忘的。
*
* 解决这一问题的办法就是使用代理模式,因为代理模式可以替代原有类的行为,所以我们要做的就是替换掉connection的close行为。
*
* 采用静态代理我们通常会使用组合的方式
*/
public class ConnectionProxy implements Connection { private Connection connection; public ConnectionProxy(Connection connection) { super(); this.connection = connection; } @Override public Statement createStatement() throws SQLException { return connection.createStatement(); } @Override public void close() throws SQLException { System.out.println("不真正关闭连接,归还给连接池"); DataSource.getInstance().recoveryConnection(connection); }
1 import java.sql.Connection;
2 import java.sql.DriverManager; 3 import java.sql.SQLException; 4 import java.util.LinkedList; 5 6 public class DataSource { 7 8 private static LinkedList<Connection> connectionList = new LinkedList<Connection>(); 9 10 static { 11 try { 12 Class.forName("com.mysql.jdbc.Driver"); 13 } catch (ClassNotFoundException e) { 14 e.printStackTrace(); 15 } 16 } 17 18 private static Connection createNewConnection() throws SQLException { 19 return DriverManager.getConnection("url", "username", "password"); 20 } 21 22 private DataSource() { 23 if (connectionList == null || connectionList.size() == 0) { 24 for (int i = 0; i < 10; i++) { 25 try { 26 connectionList.add(createNewConnection()); 27 } catch (SQLException e) { 28 e.printStackTrace(); 29 } 30 } 31 } 32 } 33 34 public Connection getConnection() throws Exception { 35 if (connectionList.size() > 0) { 36 // return connectionList.remove(); 这是原有的方式,直接返回连接,这样可能会被程序员把连接给关闭掉 37 // 下面是使用代理的方式,程序员再调用close时,就会归还到连接池 38 return new ConnectionProxy(connectionList.remove()); 39 } 40 return null; 41 } 42 43 public void recoveryConnection(Connection connection) { 44 connectionList.add(connection); 45 } 46 47 public static DataSource getInstance() { 48 return DataSourceInstance.dataSource; 49 } 50 51 private static class DataSourceInstance { 52 private static DataSource dataSource = new DataSource(); 53 } 54 55 }
从静态代理的使用上来看,我们一般是这么做的。
1,代理类一般要持有一个被代理的对象的引用。
2,对于我们不关心的方法,全部委托给被代理的对象处理。
3,自己处理我们关心的方法。
这种代理是死的,不会在运行时动态创建,因为我们相当于在编译期,也就是你按下CTRL+S的那一刻,就给被代理的对象生成了一个不可动态改变的代理类。
模拟去代理点买火车票
1 /** 2 * 代理接口 3 * 4 * @author 西忒乐 5 * 6 */ 7 public interface Iproxy { 8 9 /** 10 * 买票 11 */ 12 void buy(); 13 }
1 /** 2 * 代理类 3 * 4 * @author 西忒乐 5 * 6 */ 7 public class Proxy implements Iproxy { 8 9 private RealProxy realProxy; 10 11 public Proxy(RealProxy realProxy) { 12 super(); 13 this.realProxy = realProxy; 14 } 15 16 @Override 17 public void buy() { 18 realProxy.buy(); 19 } 20 21 }
1 /** 2 * 被代理类 3 * 4 * @author 西忒乐 5 * 6 */ 7 public class RealProxy implements Iproxy { 8 9 @Override 10 public void buy() { 11 System.out.println("我要买票!!!"); 12 } 13 14 }
1 public class Main { 2 3 public static void main(String[] args) { 4 RealProxy realProxy = new RealProxy(); 5 Proxy proxy = new Proxy(realProxy); 6 proxy.buy(); 7 } 8 9 }
静态代理对于这种,被代理的对象很固定,我们只需要去代理一个类或者若干固定的类,数量不是太多的时候,可以使用,而且其实效果比动态代理更好,因为动态代理就是在运行期间动态生成代理类,所以需要消耗的时间会更久一点。就像上述的情况,其实就比较适合使用静态代理。
动态代理是JDK自带的功能,它需要你去实现一个InvocationHandler接口,并且调用Proxy的静态方法去产生代理类。
1 public class DynamicProxy implements InvocationHandler { 2 3 private Object obj; 4 5 public DynamicProxy(Object obj) { 6 super(); 7 this.obj = obj; 8 } 9 10 @Override 11 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 12 return method.invoke(obj, args); 13 } 14 }
1 public class Main { 2 3 public static void main(String[] args) { 4 // 创建买票者 5 RealProxy zhangsan = new RealProxy(); 6 // 创建动态代理 7 DynamicProxy proxy = new DynamicProxy(zhangsan); 8 // 获取被代理的classloader 9 ClassLoader loader = zhangsan.getClass().getClassLoader(); 10 // 动态构建一个代理站点 11 IProxy iProxy = (IProxy) Proxy.newProxyInstance(loader, new Class[] { IProxy.class }, proxy); 12 // 代理点买票 13 iProxy.buy(); 14 } 15 }
动态代理模式是:通过反射机制动态的生成代理者的对象,我们在code阶段的时候,是不需要知道是谁代理谁,具体代理谁,我们会在执行的时候决定。