Java之 代理模式(Proxy)(六)

2014-11-24 07:48:24 · 作者: · 浏览: 6
stem.out.println("李四修改后订单记录没有变化:"+order);

//张三修改就不会有问题

order.setOrderNum(123, "张三");

//再次输出order

System.out.println("张三修改后,订单记录:"+order);

}

}

运行结果如下:

对不起李四,您无权修改订单中的订购数量。

李四修改后订单记录没有变化:

productName=设计模式,orderNum=100,orderUser=张三

张三修改后,订单记录:productName=设计模式,orderNum=123,orderUser=张三

从上面的运行结果就可以看出,在通过代理转调目标对象的时候,在代理对象里面,对访问的用户进行了权限判断,如果不满足要求,就不会转调目标对象的方法,从而保护了目标对象的方法,只让有权限的人操作。

11.3.3 Java中的代理

Java对代理模式提供了内建的支持,在java.lang.reflect包下面,提供了一个Proxy的类和一个InvocationHandler的接口。

通常把前面自己实现的代理模式,称为Java的静态代理。这种实现方式有一个较大的缺点,就是如果Subject接口发生变化,那么代理类和具体的目标实现都要变化,不是很灵活,而使用Java内建的对代理模式支持的功能来实现则没有这个问题。

通常把使用Java内建的对代理模式支持的功能来实现的代理称为Java的动态代理。动态代理跟静态代理相比,明显的变化是:静态代理实现的时候,在Subject接口上定义很多的方法,代理类里面自然也要实现很多方法;而动态代理实现的时候,虽然Subject接口上定义了很多方法,但是动态代理类始终只有一个invoke方法。这样当Subject接口发生变化的时候,动态代理的接口就不需要跟着变化了。

Java的动态代理目前只能代理接口,基本的实现是依靠Java的反射机制和动态生成class的技术,来动态生成被代理的接口的实现对象。具体的内部实现细节这里不去讨论。如果要实现类的代理,可以使用cglib(一个开源的Code Generation Library)。

还是来看看示例,那就修改上面保护代理的示例,看看如何使用Java的动态代理来实现同样的功能。

(1)订单接口的定义是完全一样的,就不去赘述了。

(2)订单对象的实现,只是添加了一个toString,以方便测试输出,这里也不去示例了。在前面的示例中,toString是实现在代理类里面了。

(3)直接看看代理类的实现,大致有如下变化:

  • 要实现InvocationHandler接口
  • 需要提供一个方法来实现:把具体的目标对象和动态代理绑定起来,并在绑定好过后,返回被代理的目标对象的接口,以利于客户端的操作
  • 需要实现invoke方法,在这个方法里面,去具体判断当前是在调用什么方法,需要如何处理。

    示例代码如下:

    /**

    * 使用Java中的动态代理

    */

    public class DynamicProxy implements InvocationHandler{

    /**

    * 被代理的对象

    */

    private OrderApi order = null;

    /**

    * 获取绑定好代理和具体目标对象后的目标对象的接口

    * @param order 具体的订单对象,相当于具体目标对象

    * @return 绑定好代理和具体目标对象后的目标对象的接口

    */

    public OrderApi getProxyInterface(Order order){

    //设置被代理的对象,好方便invoke里面的操作

    this.order = order;

    //把真正的订单对象和动态代理关联起来

    OrderApi orderApi = (OrderApi) Proxy.newProxyInstance(

    order.getClass().getClassLoader(),

    order.getClass().getInterfaces(),

    this);

    return orderApi;

    }

    public Object invoke(Object proxy, Method method, Object[] args)

    throws Throwable {

    //如果是调用setter方法就需要检查权限

    if(method.getName().startsWith("set")){

    //如果不是创建人,那就不能修改

    if(order.getOrderUser()!=null

    && order.getOrderUser().equals(args[1])){

    //可以操作

    return method.invoke(order, args);

    }else{

    System.out.println("对不起,"+args[1]

    +",您无权修改本订单中的数据");

    }

    }else{

    //不是调用的setter方法就继续运行

    return method.invoke(order, args);

    }

    return null;

    }

    }

    要看明白上面的实现,需要熟悉Java反射的知识,这里就不去展开了。

    (4)看看现在的客户端如何使用这个动态代理,示例代码如下:

    public class Client {

    public static void main(String[] args) {

    //张三先登录系统创建了一个订单

    Order order = new Order("设计模式",100,"张三");

    //创建一个动态代理

    DynamicProxy dynamicProxy = new DynamicProxy();

    //然后把订单和动态代理关联起来

    OrderApi orderApi = dynamicProxy.getProxyInterface(order);

    //以下就需要使用被代理过的接口来操作了

    //李四想要来修改,那就会报错

    orderApi.setOrderNum(123, "李四");

    //输出order

    System.out.println("李四修改后订单记录没有变化:"+orderApi);

    //张三修改就不会有问题

    orderApi.setOrderNum(123, "张三");

    //再次输出order

    System.out.println("张三修改后,订单记录:"+orderApi);

    }

    }

    运行结果如下:

    对不起,李四,您无权修改本订单中的数据

    李四修改后订单记录没有变化:

    productName=设计模式,orderNum=100,orderUser=张三

    张三修改后,订单记录:productName=设计模式,orderNum=123,orderUser=张三

    运行的结果跟前面完全由自己实现的代理模式是一样的。

    事实上,Java的动态代理还是实现AOP(面向方面编程)的一个重要手段,AOP的知识这里暂时不去讲述,大家先了解这一点就可以了。

    11.3.4 代理模式的优缺点

    代理模式在客户和被客户访问的对象之间,引入了一定程度的间接性,客户是直接使用代理,让代理来与被访问的对象进行交互。不同的代理类型,这种附加的间接性有不同的用途,也就是有不同的特点:

    • 远程代理:隐藏了一个对象存在于不同的地址空间的事实,也即是客户通过远程代理去访问一个对象,根本就不关心这个对象在哪里,也不关心如何通过网络去访问到这个对象,从客户的角度来讲,它只是在使用代理对象而已。
    • 虚代理:可以