代理模式

引用:代理模式【设计模式】代理模式

介绍

代理模式:为其它对象提供一种代理,以控制对这个对象的访问。

代理对象在客户端和目标对象之间起到中介的作用。

客户端通过代理类与目标对象进行交互,客户端不直接接触目标对象。

如:

租客通过中介找房东租房子,房东将房子托管给了中介,房东是目标对象,但是租赁行为是中介来执行的,中介是代理类,租客就是客户端。

中介代理房东进行租赁行为,相当于代理类对目标对象进行了增强。

示例

我们将创建一个 Image 接口和实现了 Image 接口的实体类。ProxyImage 是一个代理类,减少 RealImage 对象加载的内存占用。

ProxyPatternDemo 类使用 ProxyImage 来获取要加载的 Image 对象,并按照需求进行显示。

img

步骤 1:定义接口

创建一个接口。

// Image.java
public interface Image {
   void display();
}

步骤 2:创建实现类(真实实现类和代理类)

创建实现接口的实现类。

// RealImage.java
public class RealImage implements Image {
 
   private String fileName;
 
   public RealImage(String fileName){
      this.fileName = fileName;
      loadFromDisk(fileName);
   }
 
   @Override
   public void display() {
      System.out.println("Displaying " + fileName);
   }
 
   private void loadFromDisk(String fileName){
      System.out.println("Loading " + fileName);
   }
}
// ProxyImage.java
public class ProxyImage implements Image{
 
   private RealImage realImage;
   private String fileName;
 
   public ProxyImage(String fileName){
      this.fileName = fileName;
   }
 
   @Override
   public void display() {
      if(realImage == null){
         realImage = new RealImage(fileName);
      }
      realImage.display();
   }
}

步骤 3:使用代理类

当被请求时,使用 ProxyImage 来获取 RealImage 类的对象。

// ProxyPatternDemo.java
public class ProxyPatternDemo {
   
   public static void main(String[] args) {
      Image image = new ProxyImage("test_10mb.jpg");
 
      // 图像将从磁盘加载
      image.display(); 
      System.out.println("");
      // 图像不需要从磁盘加载
      image.display();  
   }
}

执行程序,输出结果:

Loading test_10mb.jpg
Displaying test_10mb.jpg

Displaying test_10mb.jpg

在 MyBatis 中的应用

CachingExecutor

MyBatis 中,Executor 接口用来执行 SQL:

public interface Executor {
   public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException;

   // ...
}

它有多个实现类,比如 SimpleExecutor、ReuseExecutor、BatchExecutor 和 CachingExecutor。

其中 CachingExecutor 是一个代理类,它实现了 Executor 接口,但是将 Executor 的实现委托给另一个 Executor 对象,同时添加了缓存功能:

public class CachingExecutor implements Executor {
  
  /**
   * 被代理对象
   */
  private final Executor delegate;

  public CachingExecutor(Executor delegate) {
    this.delegate = delegate;
    delegate.setExecutorWrapper(this);
  }

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler) throws SQLException {
    BoundSql boundSql = ms.getBoundSql(parameterObject);
    CacheKey key = createCacheKey(ms, parameterObject, rowBounds, boundSql);
    return query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

  @Override
  public <E> List<E> query(MappedStatement ms, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, CacheKey key, BoundSql boundSql)
      throws SQLException {
    Cache cache = ms.getCache();
    if (cache != null) {
      // ...
    }
    // 调用被代理对象执行查询
    return delegate.<E> query(ms, parameterObject, rowBounds, resultHandler, key, boundSql);
  }

  // ...
}

RoutingStatementHandler

MyBatis 中,StatementHandler 接口用来处理 Statement:

public interface StatementHandler {

  Statement prepare(Connection connection, Integer transactionTimeout)
      throws SQLException;

  // ...

}

它有多个实现类,比如 SimpleStatementHandler、PreparedStatementHandler 和 CallableStatementHandler。

RoutingStatementHandler 是一个代理类,它实现了 StatementHandler 接口,同时将 StatementHandler 的实现委托给另一个 StatementHandler 对象:

public class RoutingStatementHandler implements StatementHandler {
  private final StatementHandler delegate;

  @Override
  public Statement prepare(Connection connection, Integer transactionTimeout) throws SQLException {
    return delegate.prepare(connection, transactionTimeout);
  }

  // ...
}

在构造方法中,它根据不同的语句类型,创建不同的 StatementHandler 对象赋值给 delegate:

public RoutingStatementHandler(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {

  switch (ms.getStatementType()) {
    case STATEMENT:
      delegate = new SimpleStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    case PREPARED:
      delegate = new PreparedStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    case CALLABLE:
      delegate = new CallableStatementHandler(executor, ms, parameter, rowBounds, resultHandler, boundSql);
      break;
    default:
      throw new ExecutorException("Unknown statement type: " + ms.getStatementType());
  }

}

除此之外的所有方法实现,都委托给了 delegate 对象。

posted @ 2022-08-23 09:44  Higurashi-kagome  阅读(19)  评论(0)    收藏  举报