Java中的代理(Proxies in Java)

最近在看《Core Java》的过程中,觉得代理的部分书中写的不是很清晰,例子举的也不是很好理解,就在网上查了一份不错的讲解Java中的Proxy的资料(资料:Proxies in Java,是一份英文资料,讲解的非常清楚,我有写的不明白的地方大家可以再好好看看这里面是怎么讲的),顺便在这做个笔记。

首先要注意:代理 —- 代理的是接口(interface),而不是类(class)或是抽象类(abstract)

我们要代理的接口如下

/**
* Interface IVehicle.
*/
public interface IVehicle {
    public void start();
    public void stop();
    public void forward();
    public void reverse();
    public String getName();
}

不使用代理类(With no Proxy)

在不使用代理类的时候,我们直接定义一个Car类来实现IVehicle接口

实现代码如下:

/**
* Class Car
*/
public class Car implements IVehicle {
    private String name;
    public Car(String name) {this.name = name;}
    public void start() {
    System.out.println("Car " + name + " started");
    }
    // stop(), forward(), reverse() implemented similarly.
    // getName() not shown.
}

在执行时,直接调用Car类中实现的方法即可:

/**
* Class Client1.
* Interacts with a Car Vehicle directly.
*/
public class Client1 {
    public static void main(String[] args) {
        IVehicle v = new Car("Botar");
        v.start();    // 通过接口变量v直接调用即可
        v.forward();
        v.stop();
    }
}

所得到的结果如下:

Car Botar started
Car Botar going forward
Car Botar stopped

使用普通的代理类(With Proxy)

  • 在使用代理的方式时:
    • 用一个class VehicleProxy来实现IVehicle接口,这个VehicleProxy类就是我们的代理类
    • 在VehicleProxy中定义一个private IVehicle v来接收传进来的IVehicle c = new Car("Botar")
    • 在VehicleProxy类中把IVehicle接口中的全部方法实现一遍,不用真的都重新写一遍,只要用private IVehicle v调用即可。像这样:
// IVehicleProxy中重写的start()方法
public void start() {
    v.start();
}
  • 在主函数中调用所需方法时,不是用Car来调用,而是用代理类VehicleProxy来调用。像这样:
IVehicle c = new Car("Botar");
IVehicle v = new VehicleProxy(c);
v.start();

整体代码如下:
class VehicleProxy的实现:

/**
* Class VehicleProxy.
*/
public class VehicleProxy implements IVehicle {
    private IVehicle v;

    public VehicleProxy(IVehicle v) {this.v = v;}

    public void start() {
        System.out.println("VehicleProxy: Begin of start()");
        v.start();
        System.out.println("VehicleProxy: End of start()");
    }
    // stop(), forward(), reverse() implemented similarly.
    // getName() not shown.
}

主函数:

/**
* Class Client2.
* Interacts with a Car Vehicle through a VehicleProxy.
*/
public class Client2 {
    public static void main(String[] args) {
        IVehicle c = new Car("Botar");
        IVehicle v = new VehicleProxy(c);
        v.start();
        v.forward();
        v.stop();
    }
}

所得到的结果如下:

VehicleProxy: Begin of start()
Car Botar started
VehicleProxy: End of start()
VehicleProxy: Begin of forward()
Car Botar going forward
VehicleProxy: End of forward()
VehicleProxy: Begin of stop()
Car Botar stopped
VehicleProxy: End of stop

使用动态代理类(With Dynamic Proxy)

  • 动态代理类特点

    • 在运行中创建
    • 实现了一系列接口
    • 衍生出2个概念
      代理接口(proxy interface):代理类所实现的接口
      代理实例(proxy instance):代理类的实例
  • 每个代理实例(proxy instance)都有一个调用处理器(invocation handler object)
    这里写图片描述

  • 代理实例的创建

    • 通过Proxy.newProxyInstance()来创建一个代理实例
方法说明:
public static Object newProxyInstance(ClassLoader loader,
                                      Class[] interfaces,
                                      InvocationHandler ih)
                            throws IllegalArgumentException
参数说明:
ClassLoader loader: 类加载器,可以通过如下方法获得:
ClassLoader cl = 要代理的接口.class.getClassLoader();
Class[] interfaces: 每个元素都是代理类要实现的接口
InvocationHandler ih: 调用处理器
  • 通过创建代理实例的参数可知,每个代理实例都有一个调用处理器
  • 当一个代理实例调用一个方法时:
    • First,对这个调用方法进行编码
    • Next,调用调用处理器中的invoke()方法
invoke()
方法说明:
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable
参数说明:
Object proxy: 调用这个方法的代理实例
Method method: 与代理实例所调用的接口方法相符合的方法
Object[] args: 一个包含上述方法的所有参数的值的数组

整体代码如下:
调用处理器部分:

import java.lang.reflect.*;
/**
* Class VehicleHandler.
*/
public class VehicleHandler implements InvocationHandler {
    private IVehicle v;

    public VehicleHandler(IVehicle v) {this.v = v;}

    public Object invoke(Object proxy, Method m, Object[] args) 
    throws Throwable {
        System.out.println("Vehicle Handler: Invoking " +
        m.getName());
        return m.invoke(v, args);
    }
}

主函数:

import java.lang.reflect.*;
/**
* Class Client3.
* Interacts with a Car Vehicle through a dynamically
* generated VehicleProxy.
*/
public class Client3 {
    public static void main(String[] args) {
        IVehicle c = new Car("Botar");
        ClassLoader cl = IVehicle.class.getClassLoader();
        IVehicle v = (IVehicle) Proxy.newProxyInstance(cl,
        new Class[] {IVehicle.class}, new VehicleHandler(c));
        v.start();
        v.forward();
        v.stop();
    }
}

所得到的结果如下:

Vehicle Handler: Invoking start
Car Botar started
Vehicle Handler: Invoking forward
Car Botar going forward
Vehicle Handler: Invoking stop
Car Botar stopped

使用动态代理的原因

  • 普通的代理类有如下2个缺点:(观察使用普通的代理类的例子即可发现)
    1. 不得不在代理类VehicleProxy中实现IVehicle接口中的全部方法,这比较麻烦
    2. 代理类VehicleProxy只能代理IVehicle这一种接口,要想代理别的接口,需要写另外一个代理类

而使用动态代理则可以很好的解决上述2个问题!!!

对于动态代理:

  • 要用到需要代理的接口中的哪个方法就只代理哪个方法即可,无需实现所有要代理的接口中的全部方法
  • 可以代理任何接口
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值