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
中的AOP
,Struts2
中的拦截器都有用到,但是一说到动态代理,做Android都会想到Retrofit
,以下是Retrofit
的create()
源码
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
这段代码是不是更加明了。