加入收藏 | 设为首页 | 会员中心 | 我要投稿 安卓应用网 (https://www.0791zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 编程开发 > Java > 正文

Java设计模式之代理模式原理及实现代码分享

发布时间:2020-05-23 15:05:58 所属栏目:Java 来源:互联网
导读:简介Java编程的目标是实现现实不能完成的,优化现实能够完成的,是一种虚拟技术。生活中的方方面面都可以虚拟到代码中。代理模式所讲的就是现实生活中的这么一个概念:中介。

简介

Java编程的目标是实现现实不能完成的,优化现实能够完成的,是一种虚拟技术。生活中的方方面面都可以虚拟到代码中。代理模式所讲的就是现实生活中的这么一个概念:中介。

代理模式的定义:给某一个对象提供一个代理,并由代理对象控制对原对象的引用。

代理模式包含如下角色:

ISubject:抽象主题角色,是一个接口。该接口是对象和它的代理共用的接口。

RealSubject:真实主题角色,是实现抽象主题接口的类。

Proxy:代理角色,内部含有对真实对象RealSubject的引用,从而可以操作真实对象。代理对象提供与真实对象相同的接口,以便在任何时刻都能代替真实对象。同时,代理对象可以在执行真实对象操作时,附加其他的操作,相当于对真实对象进行封装。

实现动态代理的关键技术是反射。

静态代理

代理模式有几种,虚拟代理,计数代理,远程代理,动态代理。主要分为两类,静态代理和动态代理。静态代理比较简单,是由程序员编写的代理类,并在程序运行前就编译好的,而不是由程序动态产生代理类,这就是所谓的静态。

考虑这样的场景,管理员在网站上执行操作,在生成操作结果的同时需要记录操作日志,这是很常见的。此时就可以使用代理模式,代理模式可以通过聚合和继承两种方式实现:

/**方式一:聚合式静态代理
 * @author Goser  (mailto:goskalrie@163.com)
 * @Since 2016年9月7日
 */
//1.抽象主题接口
public interface Manager {
  void doSomething();
}
//2.真实主题类
public class Admin implements Manager {
  public void doSomething() {
    System.out.println("Admin do something.");
  }
}
//3.以聚合方式实现的代理主题
public class AdminPoly implements Manager{
  private Admin admin;
  
  public AdminPoly(Admin admin) {
    super();
    this.admin = admin;
  }
 
  public void doSomething() {
    System.out.println("Log:admin操作开始");
    admin.doSomething();
    System.out.println("Log:admin操作结束");
  }
}
//4.测试代码
    Admin admin = new Admin();
    Manager m = new AdminPoly(admin);
    m.doSomething();
//方式二:继承式静态代理
//与上面的方式仅代理类和测试代码不同
//1.代理类
public class AdminProxy extends Admin {
  @Override
  public void doSomething() {
    System.out.println("Log:admin操作开始");
    super.doSomething();
    System.out.println("Log:admin操作开始");
  }
}
//2.测试代码
    AdminProxy proxy = new AdminProxy();
    proxy.doSomething();

聚合实现方式中代理类聚合了被代理类,且代理类及被代理类都实现了同一个接口,可实现灵活多变。继承式的实现方式则不够灵活。

比如,在管理员操作的同时需要进行权限的处理,操作内容的日志记录,操作后数据的变化三个功能。三个功能的排列组合有6种,也就是说使用继承要编写6个继承了Admin的代理类,而使用聚合,仅需要针对权限的处理、日志记录和数据变化三个功能编写代理类,在业务逻辑中根据具体需求改变代码顺序即可。

动态代理

一般来说,对代理模式而言,一个主题类与一个代理类一一对应,这也是静态代理模式的特点。

但是,也存在这样的情况,有n各主题类,但是代理类中的“前处理、后处理”都是一样的,仅调用主题不同。也就是说,多个主题类对应一个代理类,共享“前处理,后处理”功能,动态调用所需主题,大大减小了程序规模,这就是动态代理模式的特点。

JDK动态代理

实现

//1. 抽象主题
public interface Moveable {
  void move() throws Exception;
}
//2. 真实主题
public class Car implements Moveable {
  public void move() throws Exception {
    Thread.sleep(new Random().nextInt(1000));
    System.out.println("汽车行驶中…");
  }
}
//3.事务处理器
public class TimeHandler implements InvocationHandler {
  private Object target;
  public TimeHandler(Object target) {
    super();
    this.target = target;
  }
  /**
   * 参数:
   *proxy 被代理的对象
   *method 被代理对象的方法
   *args 方法的参数
   * 返回:
   *Object 方法返回值
   */
  public Object invoke(Object proxy,Method method,Object[] args)
      throws Throwable {
    long startTime = System.currentTimeMillis();
    System.out.println("汽车开始行驶…");
    method.invoke(target,args);
    long stopTime = System.currentTimeMillis();
    System.out.println("汽车结束行驶…汽车行驶时间:" + (stopTime - startTime) + "毫秒!");
    return null;
  }
}
//测试类
public class Test {
  public static void main(String[] args) throws Exception{
    Car car = new Car();
    InvocationHandler h = new TimeHandler(car);
    Class<!--?--> cls = car.getClass();
    /**
     *loader 类加载器
     *interfaces 实现接口
     *h InvocationHandler
     */
    Moveable m = (Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),h);
    m.move();
  }
}

代码讲解:

在测试代码中,Proxy.newProxyInstance()方法需要3个参数:类加载器(要进行代理的类)、被代理类实现的接口,事务处理器。所以先实例化Car,实例化InvocationHandler的子类TimeHandler,将各参数传入Proxy的静态方法newProxyInstance()即可获得Car的代理类,前面的静态代理,代理类是我们编写好的,而动态代理则不需要我们去编写代理类,是在程序中动态生成的。

JDK动态代理步骤

1.创建一个实现InvocationHandler接口的类,它必须实现invoke()方法

2.创建被代理的类及接口

3.调用Proxy的静态方法,创建一个代理类

4.通过代理调用方法

而为什么要进行如此操作,可以从Proxy和InvocationHandler的源码中找打答案。对源码不感兴趣的可以将下面的源码部分小节略过。

JDK动态代理原理与源码

newProxyInstance()方法的源码:

public static Object newProxyInstance(ClassLoader loader,Class<!--?-->[] interfaces,InvocationHandler h)
    throws IllegalArgumentException{
    if (h == null) {
      throw new NullPointerException();
    }
    final Class<!--?-->[] intfs = interfaces.clone();
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
      checkProxyAccess(Reflection.getCallerClass(),loader,intfs);
    }
    /*查找或生成指定的代理类*/
    Class<!--?--> cl = getProxyClass0(loader,intfs);
    /*用指定的调用处理程序调用它的构造函数.*/
    try {
    //获得类的构造函数
      final Constructor<!--?--> cons =cl.getConstructor(constructorParams);
      final InvocationHandler ih = h;
      if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
       //当需要代理的类实现了一个非public的接口时,因为这样的接口需要特殊的权限,因此调用doPrivilege(native 修饰的方法)创建代理实例。
        return AccessController.doPrivileged(newPrivilegedAction<object>() {
          public Object run() {
            return newInstance(cons,ih);
          }
        });
      } else {
        return newInstance(cons,ih);
      }
    } catch (NoSuchMethodException e) {
      throw new InternalError(e.toString());
    }
}

可以看到,获得代理类的代码是

Classcl = getProxyClass0(loader,intfs);

并由此获得代理类的构造函数,生成代理类的实例返回给该方法的调用者。

继续跟进getProxyClass0()方法:

/** 生成代理类。调用该方法前必须使用checkproxyaccess方法执行权限检查。*/
  private static Class<!--?--> getProxyClass0(ClassLoader loader,Class<!--?-->... interfaces) {
  //检查实现的接口数,65535这个数字好特殊,端口数好像也是这个,这个数字是由虚拟机所决定的,2^16-1个
  if (interfaces.length > 65535) {
      throw new IllegalArgumentException("interface limit exceeded");
    }
    // 如果代理类已经通过实现给定接口的类加载器创建了,则返回缓存中的该类的副本;否则将通过ProxyClassFactory创建代理类
    return proxyClassCache.get(loader,interfaces);
}

还是没有看到代理类是怎么生成的,只知道代理类是从proxyClassCache中取得的,这个变量是与缓存相关的一个对象,查看该变量的声明与初始化:

private static final WeakCache<classloader,>[],Class<!--?-->>
    proxyClassCache = new WeakCache<>(new KeyFactory(),new ProxyClassFactory());</classloader,>

可以发现proxyClassCache是个用来缓存代理类的类变量,大家知道类变量的特点是与类一一对应,在一个虚拟机中类只有一个,对应着在一个虚拟机中类变量也只有一个,且在此处,在Proxy类被加载的时候就赋值了。在赋值操作的参数中有ProxyClassFactory()这么一个构造函数,这个是动态代理中的关键:生成代理类的类文件字节码。继续跟进去,找到代理类的生成之处了:

  /** 根据给定的类加载器和接口数组生成代理类的工厂类*/
  private static final class ProxyClassFactory
    implements BiFunction<classloader,class<?>[],Class<!--?-->>
  {
    // 所有代理类名称的前缀
    private static final String proxyClassNamePrefix = "$Proxy";
 
    //用于生成唯一代理类名称的下一个序号
    private static final AtomicLong nextUniqueNumber = new AtomicLong();
 
    @Override
    public Class<!--?--> apply(ClassLoader loader,Class<!--?-->[] interfaces) {
 
      Map<class<?>,Boolean>interfaceSet = new IdentityHashMap<>(interfaces.length);
      for (Class<!--?--> intf : interfaces) {
        /* 验证类加载器将此接口的名称解析为实际对象的名称。*/
        Class<!--?--> interfaceClass =null;
        try {
          interfaceClass = Class.forName(intf.getName(),false,loader);
        } catch (ClassNotFoundException e) {
        }
        if (interfaceClass != intf) {
          throw new IllegalArgumentException(
            intf + " is not visible from classloader");
        }
        /* 验证类对象确实是一个接口。*/
        if (!interfaceClass.isInterface()) {
          throw new IllegalArgumentException(
            interfaceClass.getName() + " is not an interface");
        }
        /*确保接口唯一*/
        if (interfaceSet.put(interfaceClass,Boolean.TRUE) != null) {
          throw new IllegalArgumentException(
            "repeated interface: " + interfaceClass.getName());
        }
      }
 
      String proxyPkg = null;   // 代理类的包名
 
      /*记录非公开代理接口的包,以便将代理类定义在同一个包中。确认所有非公共代理接口都在同一个包中。*/
      for (Class<!--?--> intf : interfaces) {
        int flags = intf.getModifiers();
        if (!Modifier.isPublic(flags)) {
          String name =intf.getName();
          int n = name.lastIndexOf('.');
          String pkg = ((n == -1) ? "" : name.substring(0,n + 1));
          if (proxyPkg == null) {
            proxyPkg = pkg;
          } else if (!pkg.equals(proxyPkg)) {
            throw new IllegalArgumentException(
              "non-public interfaces fromdifferent packages");
          }
        }
      }
 
      if (proxyPkg == null) {
        // 如果没有非公开的代理接口,使用com.sun.proxy作为包名
        proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
      }
 
      /* 生成代理类名的序号*/
      long num = nextUniqueNumber.getAndIncrement();
      //生成全类名
      String proxyName = proxyPkg + proxyClassNamePrefix + num;
 
      /*生成代理类字节码 */
      byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,interfaces);
      try {
        return defineClass0(loader,proxyName,proxyClassFile,proxyClassFile.length);
      } catch (ClassFormatError e) {
        throw new IllegalArgumentException(e.toString());
      }
    }
}

(编辑:安卓应用网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读