• 代理模式及其应用


    代理模式是一种应用十分广泛的结构型模式,在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    @Override
    11    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    @Override
    11    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 }

    注:以上摘抄自http://www.runoob.com/design-pattern/proxy-pattern.html

    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     @Override
    4     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                     @Override
    19                     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的简单实现。

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

  • 相关阅读:
    PYQT5学习笔记之各模块介绍
    socket网络编程
    python异常处理
    面向对象三大特性:继承,多态,封装
    面向对象编程
    解密for循环工作机制之迭代器,以及生成器、三元表达式与列表解析、解压序列
    文件处理之处理模式及其黑魔法
    php对xml文件的增删改查
    jquery.zclip实现点击拷贝文字功能
    php 验证访问浏览器是电脑还是手机
  • 原文地址:https://www.cnblogs.com/jy107600/p/8657217.html
Copyright © 2020-2023  润新知