第十五章 代理模式

132 阅读4分钟

1、代理模式的定义

给某一个对象提供一个代理或占位符,并由代理对象来控制对源对象的访问。

2、代理模式的结构

image.png 1、 Subject(抽象主题角色)

声明主题对象与代理主题的共同接口,这样在真是对象所使用的地方都可以用代理对象代替,客户端通常对抽象主题对象进行编程。

2、 Proxy(代理主题角色)

包含对真实对象的引用,控制主题对象的使用,除主题对象外,执行其他操作。

3、 RealSubject(真实主题角色)

实现了真实的业务操作。

3、代理模式的实现

3.1、静态代理模式的实现

Subject接口

package designpatterns.proxy.staticProxy;

/**
 * 抽象接口
 */
public interface Subject {

   void request();
}

RealSubject类

package designpatterns.proxy.staticProxy;

/**
 * 真实被代理的类
 */
public class RealSubject implements Subject{
    @Override
    public void request() {
        System.out.println("我是真实角色");
    }
}

Proxy类

package designpatterns.proxy.staticProxy;

/**
 * 代理对象
 */
public class Proxy implements Subject{

    private RealSubject realSubject;

    public Proxy(RealSubject realSubject){
      this.realSubject = realSubject;
    }

    @Override
    public void request() {
        preRequest();
        realSubject.request();
        postRequest();
    }


    public void preRequest(){
        System.out.println("请求前——————————");
    }

    public void postRequest(){
        System.out.println("请求后——————————");
    }
}

Client客户端测试类

package designpatterns.proxy.staticProxy;

/**
 * 客户端调用
 */
public class Client {

    public static void main(String[] args) {
        new Proxy(new RealSubject()).request();
    }
}

3.2、动态代理实现

同一个代理类能够代理多个不同的真实主题类而且可以代理不同的方法。

3.2.1 Proxy类

提供了用于创建动态代理类和实例对象的方法

//返回Class类型的代理类,需要类加载器和指定代理的接口数组(与真实类的接口列表一致)
public static Class <?> getProxyClass(ClassLoader loader,Class <?>...interfaces);
//动态创建代理类实例,类加载器,接口列表,指派调用处理程序类
public static Object newProxyInstance(ClassLoader,Class<?>[] interfaces,InvocationHandler h)

3.2.2 InvocationHandler接口

代理处理程序类接口,该接口做为代理实例的调用处理者的公共父类,每个代理类的实例都可以提供一个相关的具体调用处理者

//当代理实例中的业务方法调用时候自动调用该方法
public Object invoke(Object proxy,Method method,Object[] args)

3.2.3 动态代理实现

实现日志记录 AbstractUserDao:抽象用户主题角色

package designpatterns.proxy.dynamic;

//抽象UserDAO:抽象主题角色
public interface AbstractUserDAO {
   public Boolean findUserById(String userId);
}

AbstractDocumentDao:抽象文档主题角色

package designpatterns.proxy.dynamic;

//抽象DocumentDAO:抽象主题角色
public interface AbstractDocumentDAO {
   public Boolean deleteDocumentById(String documentId);
}

UserDao:用户具体角色

package designpatterns.proxy.dynamic;

//具体UserDAO类:真实角色
public class UserDAO implements AbstractUserDAO {
   public Boolean findUserById(String userId) {
      if (userId.equalsIgnoreCase("张无忌")) {
         System.out.println("查询ID为" + userId + "的用户信息成功!");
         return true;
      }
      else {
         System.out.println("查询ID为" + userId + "的用户信息失败!");
         return false;
      }
   }
}

DocumentDao 文档具体角色

package designpatterns.proxy.dynamic;

//具体DocumentDAO类:真实角色
public class DocumentDAO implements AbstractDocumentDAO {
   public Boolean deleteDocumentById(String documentId) {
      if (documentId.equalsIgnoreCase("D001")) {
         System.out.println("删除ID为" + documentId + "的文档信息成功!");
         return true;
      }
      else {
         System.out.println("删除ID为" + documentId + "的文档信息失败!");
         return false;
      }
   }
}

DaoLogHandler :请求处理程序

package designpatterns.proxy.dynamic;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Calendar;
import java.util.GregorianCalendar;

//自定义请求处理程序类
public class DAOLogHandler implements InvocationHandler {
   private Calendar calendar;
   private Object object;
   
   public DAOLogHandler() {   
   }
   
   //自定义有参构造函数,用于注入一个需要提供代理的真实主题对象
   public DAOLogHandler(Object object) {
      this.object = object;
   }
   
   //实现invoke()方法,调用在真实主题类中定义的方法
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      beforeInvoke();
      Object result = method.invoke(object, args); //转发调用
      afterInvoke();
      return result;
   }

   //记录方法调用时间
   public void beforeInvoke(){
      calendar = new GregorianCalendar();
      int hour = calendar.get(Calendar.HOUR_OF_DAY);
      int minute = calendar.get(Calendar.MINUTE);
      int second = calendar.get(Calendar.SECOND);
      String time = hour + ":" + minute + ":" + second;
      System.out.println("调用时间:" + time);
   }

   public void afterInvoke(){
      System.out.println("方法调用结束!" );
   }
}

Client :测试类

package designpatterns.proxy.dynamic;

import java.lang.reflect.Proxy;
import java.lang.reflect.InvocationHandler;

public class Client {
   public static void main(String args[]) {
      InvocationHandler handler = null;
      AbstractUserDAO userDAO = new UserDAO();
      handler = new DAOLogHandler(userDAO);
      AbstractUserDAO proxy = null;
      
        //动态创建代理对象,用于代理一个AbstractUserDAO类型的真实主题对象
      proxy = (AbstractUserDAO)Proxy.newProxyInstance(AbstractUserDAO. class.getClassLoader(), new Class[]{AbstractUserDAO.class}, handler);
       proxy.findUserById("张无忌"); //调用代理对象的业务方法
    
       System.out.println("------------------------------");
       
      AbstractDocumentDAO docDAO = new DocumentDAO();
      handler = new DAOLogHandler(docDAO);
      AbstractDocumentDAO proxy_new = null;
      
      //动态创建代理对象,用于代理一个AbstractDocumentDAO类型的真实主题对象
      proxy_new = (AbstractDocumentDAO)Proxy.newProxyInstance(AbstractDocumentDAO.class.getClassLoader(), new Class[]{AbstractDocumentDAO.class}, handler);
       proxy_new.deleteDocumentById("D002"); //调用代理对象的业务方法
   } 
}

4、代理模式的优缺点及适用环境

优点:

  • 减低系统的耦合度
  • 符合开闭原则,较好的灵活性和可扩展性

缺点:

  • 请求处理速度变慢
  • 代理模式实现较为复杂

适用环境:

  • 访问远程主机对象使用远程代理
  • 需要资源较少对象代替资源较多对象的时候使用**
  • 某个频繁操作的结果踢动临时空间,以供多个客户端共享范文这些结果时候使用共享代理
  • 不同用户不同访问权限的时候使用保护代理
  • 需要为一个对象访问的引用提供一些额外操作的时候使用智能引用代理

5、代理分类

远程代理

客户端程序访问远程主机上的对象(远程主机有更好的性能,快速响应),远程代理可以将网络的细节隐藏起来。jRMI(远程方法调用)能够实现一个java虚拟机中的对象,调用另一个java 虚拟机中的对象

虚拟代理

对一些占用系统资源较多或者加载时间较长的对象提供虚拟带来,真实对象创建成功之前虚拟对象扮演真实对象的替身,而当真实兑现创建后虚拟对象将用户的请求转发给真实对象。