Java中的代理模式

Java中的代理模式

代理模式的定义:对其他对象提供一种代理以控制对这个对象的访问。
形象的举个例子:

客户想买一辆车,并不是去汽车制造厂买车,而是去4s店买车,4s在充当了一个代理的角色,汽车制造厂让4s代理销售汽车。

此处输入图片的描述

原则上说客户可以从汽车厂商直接购买汽车,但是现实生活中这种方式很少见,厂商一般会委托4S店进行销售,客户直接与4S打交道,其实4S就充当了一个中间人的角色。

在软件设计中,代理模式是一个很常见的模式,UML图如下:
此处输入图片的描述

  • 用户只关心接口功能,如图的Subject。
  • 接口的真正实现者是RealSubject,但它并不直接与用户(client)接触,而是通过代理。
  • ProxySubject就是代理,它实现了Subject,在它内部引用了RealSubject,在调用接口功能的时候可以增强操作。

在Java代理模式中有静态代理跟动态代理。

静态代理

以客户想买一辆车为例:
需要一个接口,接口提供的功能就是买车(用户关心的只是买辆车)。

public interface IBuyCar {
    void buyCar();
}

然后需要一个真正实现这个接口的类(客户),跟一个实现接口的代理类(4S店)。

public class Customer implements IBuyCar {
    @Override
    public void buyCar() {
        System.out.println("用户买了一辆车");
    }
}

声明代理:

public class Car4sShop implements IBuyCar{

    private Customer mCustomer;

    public Car4sShop(Customer customer) {
        mCustomer = customer;
    }

    @Override
    public void buyCar() {
        System.out.println("4s店给用户加装了装潢");
        mCustomer.buyCar();
        System.out.println("4s店给客户上了保险");
    }
}

Car4sShop就是Proxy代理对象,在调用buyCar()的时候,强行给客户加了装潢,上了保险。

测试一下代码:

public class Main {
    public static void main(String[] args) {        
        Customer customer = new Customer();
        IBuyCar iBuyCar = new Car4sShop(customer);
        iBuyCar.buyCar();
    }
}

打印结果:

4s店给用户加装了装潢
用户买了一辆车
4s店给客户上了保险

代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。

此为静态代理,静态代理的特点是代理类自己实现API接口,但是每一个方法调用的却是传入的api对象里的。(例如上面Car4sShop实现了buyCar方法,但是调用buyCar方法的时候,却是调用Customer的buyCar。)

优点:
可以做到在不修改目标对象的功能前提下,对目标功能扩展。

缺点:
因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类。同,一旦接口增加方法,目标对象与代理对象都要维护。

动态代理

动态代理模式可以使得我们在不改变原来已有的代码结构的情况下,对原来的“真实方法”进行扩展、增强其功能

同样以客户要买一辆车为例:
需要一个接口,接口提供的功能就是买车:

public interface IBuyCar {
    void buyCar();
}

然后需要一个真正实现这个接口的类(客户)

public class Customer implements IBuyCar {
    @Override
    public void buyCar() {
        System.out.println("用户买了一辆车");
    }
}

声明动态代理:

public class DynamicProxy implements InvocationHandler {
    /**
     * 被代理类的实例
     */
    private Object obj;

    public DynamicProxy(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println(method.getName() + "之前");    
        Object result = method.invoke(this.obj, args);
        System.out.println(method.getName() + "之后");
        return result;
    }

}

测试代码:

public class Main {
    public static void main(String[] args) {        
        Customer customer = new Customer();
        DynamicProxy dynamicProxy = new DynamicProxy(customer);
        IBuyCar iBuyCar = (IBuyCar) Proxy.newProxyInstance(Customer.class.getClassLoader(), Customer.class.getInterfaces(), dynamicProxy);
        iBuyCar.buyCar();
    }
}

输出结果:

buyCar之前
用户买了一辆车
buyCar之后

什么是InvocationHandler ?

InvocationHandler 是一个接口,官方文档解释说,每个代理的实例都有一个与之关联的 InvocationHandler 实现类,如果代理的方法被调用,那么代理便会通知和转发给内部的 InvocationHandler 实现类,由它决定处理。

public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

InvocationHandler 内部只是一个 invoke() 方法,正是这个方法决定了怎么样处理代理传递过来的方法调用。

  • proxy 代理对象
  • method 代理对象的方法
  • args 代理对象方法的参数

因为,Proxy 动态产生的代理会调用 InvocationHandler 实现类,所以 InvocationHandler 是实际执行者。

写到这,觉得动态代理的优势并没有体现出来,改一下需求,现在客户不止要买车,再加一个功能,“议价”,代码如下:

public interface IBuyCar {
    void buyCar();

    void bargain();
}
public class Customer implements IBuyCar {
    @Override
    public void buyCar() {
        System.out.println("用户买了一辆车");
    }

    @Override
    public void bargain() {
        System.out.println("客户讲价了");
    }
}

如果是静态代理的话,那么所有的代理都需要修改了,但是动态代理不需要改。

测试代码:

public class Main {
    public static void main(String[] args) {        
        Customer customer = new Customer();
        DynamicProxy dynamicProxy = new DynamicProxy(customer);
        IBuyCar iBuyCar = (IBuyCar) Proxy.newProxyInstance(Customer.class.getClassLoader(), Customer.class.getInterfaces(), dynamicProxy);
        iBuyCar.buyCar();
        iBuyCar.bargain();
    }
}

输出结果:

buyCar之前
用户买了一辆车
buyCar之后
bargain之前
客户讲价了
bargain之后

动态代理语法

动态代码涉及了一个非常重要的类 Proxy。正是通过 Proxy 的静态方法 newProxyInstance 才会动态创建代理。

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException {
  • loader 类加载器
  • interfaces 需要代理的接口
  • h InvocationHandler对象
    如:
    public static void main(String[] args) {        
        Customer customer = new Customer();
        DynamicProxy dynamicProxy = new DynamicProxy(customer);
        IBuyCar iBuyCar = (IBuyCar) Proxy.newProxyInstance(Customer.class.getClassLoader(), Customer.class.getInterfaces(), dynamicProxy);
        iBuyCar.buyCar();
    }

后续

动态代理在Spring中的AOPStruts2中的拦截器都有用到,但是一说到动态代理,做Android都会想到Retrofit,以下是Retrofitcreate()源码

  public <T> T create(final Class<T> service) {
    Utils.validateServiceInterface(service);
    if (validateEagerly) {
      eagerlyValidateMethods(service);
    }
    return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class<?>[] { service },
        new InvocationHandler() {
          private final Platform platform = Platform.get();

          @Override public Object invoke(Object proxy, Method method, @Nullable Object[] args)
              throws Throwable {
            // If the method is a method from Object then defer to normal invocation.
            if (method.getDeclaringClass() == Object.class) {
              return method.invoke(this, args);
            }
            if (platform.isDefaultMethod(method)) {
              return platform.invokeDefaultMethod(method, service, proxy, args);
            }
            ServiceMethod<Object, Object> serviceMethod =
                (ServiceMethod<Object, Object>) loadServiceMethod(method);
            OkHttpCall<Object> okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

了解完动态代理之后,看Retrofit这段代码是不是更加明了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值