代理模式及其应用
发布日期:2021-05-09 04:07:20 浏览次数:15 分类:博客文章

本文共 5521 字,大约阅读时间需要 18 分钟。

代理模式是一种应用十分广泛的结构型模式,在spring中,就有使用了代理模式,今天我们来总结一下代理模式,主要分析其原理,还有在特定场景下是怎样应用的。

意图:为其他对象提供一种代理以控制对这个对象的访问。

主要解决:在直接访问对象时带来的问题,比如说:要访问的对象在远程的机器上。在面向对象系统中,有些对象由于某些原因(比如对象创建开销很大,或者某些操作需要安全控制,或者需要进程外的访问),直接访问会给使用者或者系统结构带来很多麻烦,我们可以在访问此对象时加上一个对此对象的访问层。

何时使用:想在访问一个类时做一些控制。

如何解决:增加中间层。

关键代码:实现与被代理类组合。

注意:与适配器模式的区别,适配器模式主要改变所考虑对象的接口,而代理模式不能改变所代理类的接口。与装饰器模式的区别,装饰器模式是为了增强功能,代理模式是为了加以控制。

我们来看一个例子:

我们创建一个image接口来抽象display这一行为,然后通过proxyImage来获取要加载的图像并显示。

1.创建接口代码:

1 public interface Image {2    void display();3 }

2.创建实体类(被代理类也叫真实类):

1 public class RealImage implements Image { 2  3    private String fileName; 4  5    public RealImage(String fileName){ 6       this.fileName = fileName; 7       loadFromDisk(fileName); 8    } 9 10    @Override11    public void display() {12       System.out.println("Displaying " + fileName);13    }14 15    private void loadFromDisk(String fileName){16       System.out.println("Loading " + fileName);17    }18 }

3.创建代理类:

1 public class ProxyImage implements Image{ 2  3    private RealImage realImage;   //持有一个指向真实类的引用 4    private String fileName; 5  6    public ProxyImage(String fileName){ 7       this.fileName = fileName; 8    } 9 10    @Override11    public void display() {   //这里实现了对图片的延时加载,在代理类被执行的时候,图片才加载到内存12       if(realImage == null){13          realImage = new RealImage(fileName);14       }15       realImage.display();16    }17 }

4.调用类:

1 public class ProxyPatternDemo { 2      3    public static void main(String[] args) { 4       Image image = new ProxyImage("1.jpg"); 5  6       //图像将从磁盘加载 7       image.display();  8       System.out.println(""); 9       //图像将不从磁盘加载10       image.display();     11    }12 }

注:以上摘抄自

5.应用:

在数据库连接池当中,数据库的连接在整个应用的启动期间,几乎是不关闭的。

但我们在编写代码的时候,即使用到了连接池,也会去执行connection.close()方法,这样频繁的关闭创建连接,十分的浪费资源,这就带来了问题,我们执行close() 方法,真的是关闭了这个连接吗?

我们以c3p0连接池为例,研究一下里面到底是如何执行close()方法的:

首先,我们先看一下在ComboPooledDataSource中取得的数据库连接是什么东西:

1 import java.beans.PropertyVetoException; 2 import java.sql.SQLException; 3  4 /** 5  * Created by jy on 2018/3/27. 6  */ 7 public class c3p0Test { 8     public static void main(String[] args) throws SQLException, PropertyVetoException { 9         ComboPooledDataSource comboPooledDataSource = new ComboPooledDataSource();10         comboPooledDataSource.setDriverClass("com.mysql.jdbc.Driver");11         comboPooledDataSource.setJdbcUrl("jdbc:mysql://localhost:3306/qingguodb?characterEncoding=utf-8");12         comboPooledDataSource.setPassword("root");13         comboPooledDataSource.setUser("root");14         System.out.println(comboPooledDataSource.getConnection().getClass());15     }16 }

我们看一下执行结果:

我们来看一下这个类的层次结构:

可以看到,这个类实现了Connection接口。也就是connection的代理类,我们看一下这个类中的close() 方法:

1 public final class NewProxyConnection implements Connection, C3P0ProxyConnection 2 { 3     protected Connection inner; 4     //.... 5 public synchronized void close() throws SQLException 6     { 7         try 8         { 9             if (! this.isDetached())10             {11                 NewPooledConnection npc = parentPooledConnection;12                 this.detach();13                 npc.markClosedProxyConnection( this, txn_known_resolved );   //同步确认连接关闭14                 this.inner = null;    //直接置为null,并没有调用inner.close()15                           //.....16             }17     }

 

我们看到代码中是直接把newProxyConnection中的属性inner直接置为空,并没有close 真正的connection,所以,我们可以知道,在数据库连接池C3P0中,底层通过使用了代理模式,生成代理类,来管理真正的数据库连接,当我们执行close()方法时,实际上是执行了代理类的close()方法,并不会真正的关闭数据库连接。

我们上面总结的代理模式的代理类是硬编码的,一般来说,代理类持有一个被代理类的对象的引用,我们关心的业务逻辑,放在代理类中去执行,如果有大量的类需要被代理,那我们是不是就要写大量对应的代理类呢?于是,上面这个称作是静态代理,那对应的就有动态代理。我们来看看一下什么是动态代理。

在java的动态代理机制中,有一个重要的接口:InvocationHandler接口,它为每个代理类都关联了一个handler,当我们通过代理对象调用一个方法的时候,就会被转发到InvocationHandler这个接口的invoke方法来进行调用,我们来看看这个方法:

1 Object invoke(Object proxy, Method method, Object[] args) throws Throwable

其中:proxy指的是代理对象,method指的我们要调用被代理对象的某个方法,args指的是调用被代理对象方法时需要传入的参数。

 

我们来通过一个例子看看到底如何实现动态代理:

1.定义主题接口:

1 public interface Subject {2     void sayHello();3 }

2.定义真实主题(被代理的类):

1 public class RealSubject implements Subject{2 3     @Override4     public void sayHello() {5         System.out.println("hello world");6     }7 }

3.定义代理生成工厂:

1 public class ProxyFactory{ 2  3     //维护一个目标对象 4     private Object target; 5  6     //为目标对象赋值 7     public ProxyFactory(Object target){ 8         this.target=target; 9     }10 11     //给目标对象生成代理对象12     public Object getProxyInstance(){13         //传入被代理类的类加载器,被代理类实现的接口中的方法,需要添加的业务逻辑,返回一个代理类14         return Proxy.newProxyInstance(15                 target.getClass().getClassLoader(),16                 target.getClass().getInterfaces(),17                 new InvocationHandler() {18                     @Override19                     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {20                         //目标对象执行前加入业务逻辑21                         System.out.println("我要开始说话了:");22                         //执行目标对象方法23                         Object value = method.invoke(target, args);24                         //目标对象执行后加入逻辑25                         System.out.println("我说完了");26                         return value;27                     }28                 }29         );30     }31 32 }

4.调用函数:

1 public class Client {2     public static void main(String[] args) {3         Subject realSubject = new RealSubject();4         ((Subject)new ProxyFactory(realSubject).getProxyInstance()).sayHello();5     }6 }

我们看一下输出:

我们发现,在执行sayHello()前后的业务都被织入了进去。原来这就是spring aop的简单实现。

关于代理模式我想就总结到这里,至于动态代理的实现原理及实现方式,这些内容与代理模式本身没有多大的关联,我想放到下一片文章中去总结。

 

上一篇:一起来读Netty In Action之异步和事件驱动(一)
下一篇:java中自定义注解的应用

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2025年03月22日 12时59分49秒