代理在生活中非常常见,如我们平时买东西,我们不会去工厂买,而是去超市或者网购。超市和网购平台实际上就是一种代理,他们代理了我们去向工厂或者向中间商购买和运输的过程。这是生活中的代理,那么我们开发中的代理模式是怎样的呢?
代理模式
定义
为其他对象提供一种代理,以控制这个对象的访问。
涉及角色及说明
Subject(抽象主题类):接口或抽象类,声明真实主题与代理的共同接口方法。
RealSubject(真实主题类):也被叫做代理类或被委托类,定义了代理所表示的真实对象,负责具体业务逻辑的执行,客户端可以通过代理类间接的调用真实主题类的方法。
Proxy(代理类):也叫委托类,持有对真实主题的引用,在其所实现的接口中调用真实主题类中相应的接口方法执行。
Client(客户端类):使用代理模式的地方
以日常购买东西为例子,消费者只需要关心购买东西(静态代理
)
抽象主题类
定义了真实主题与代理的共同接口方法
public interface IBuy {
void buy();
}
真实主题类
消费者只关心买东西
public class Consumer implements IBuy {
@Override
public void buy() {
System.out.println("消费者买东西");
}
}
代理类
持有真实主题类的引用,为真实主题类提供额外服务
public class Supermarket implements IBuy {
private IBuy consumer;
public Supermarket(IBuy consumer) {
this.consumer = consumer;
}
@Override
public void buy() {
System.out.println("从工厂买回来");
consumer.buy();
System.out.println("提供售后服务");
}
}
客户端
public class Client {
public static void main(String[] args) {
Consumer consumer = new Consumer();
Supermarket supermarket = new Supermarket(consumer);
supermarket.buy();
}
}
输出结果:
从工厂买回来
消费者买东西
提供售后服务
小结一下
静态代理的代理类在编译时期已经存在,一般需要先定义接口,被代理对象和代理对象共同实现这个接口
优点:
1.职责清晰,被代理角色只实现实际的业务逻辑,代理对象实现附加的处理逻辑
2.扩展性高,可以更换不同的代理类,实现不同的代理逻辑
缺点也比较明显
如果代理的业务类型增加,需要新增接口方法,同时代理类和被代理类都需要对应的去新增方法。如果新增的业务量非常多,那么代理类的代码将会暴增,可读性降低难以维护。
同样以日常购买东西为例子(动态代理
)
代理类
public class BuyInvocationHandler implements InvocationHandler {
private IBuy mConsumer;
public BuyInvocationHandler(IBuy mConsumer) {
this.mConsumer = mConsumer;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("从工厂买回来");
Object returnObject = method.invoke(mConsumer, args);
System.out.println("提供售后服务");
return returnObject;
}
}
真实主题类
public class Consumer implements IBuy {
@Override
public void buy() {
System.out.println("消费者买东西");
}
}
客户端
public class Client {
public static void main(String[] args) {
Consumer consumer = new Consumer();
IBuy consumerBuy = (IBuy) Proxy.newProxyInstance(consumer.getClass().getClassLoader(), consumer.getClass().getInterfaces(),
new BuyInvocationHandler(consumer));
consumerBuy.buy();
}
}
输出结果:
从工厂买回来
消费者买东西
提供售后服务
小结一下
动态代理是指运行时动态生成一个代理对象,相对于编译时的静态来区分。动态代理可以在运行时动态创建一个类,实现一个或多个接口,可以在不修改原有类的基础上动态为通过该类获取的对象添加方法、修改行为。
这个动态生成的代理类帮我们做一些逻辑处理,主要是利用字节码技术创建Proxy类然后通过反射创建实例。
InvocationHandler是动态代理接口,动态代理类需要实现该接口,并在invoke方法中对代理类的方法进行处理
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
参数说明
*Object proxy:被代理的对象
*Method method:要调用的方法
*Object[] args:方法调用所需要的参数
Proxy类可以通过newProxyInstance创建一个代理对象
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
Class>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
Class> cl = getProxyClass0(loader, intfs);
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
cons.setAccessible(true);
}
return cons.newInstance(new Object[]{h});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
参数说明:
*ClassLoader loader:类加载器
*Class>[] interfaces:要代理的接口
*InvocationHandler h:实现InvocationHandler接口的子类
生成的代理类大概如下:
public final class $Proxy0 extends Proxy implements IBuy {
//这4个Method对象分别代表equals()、toString()、buy()、hashCode()方法
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
//构造方法接收一个InvocationHandler对象为参数,这个对象就是代理类的“直接委托类”(真正的委托类可以看做代理类的“间接委托类”)
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
//对equals方法的调用实际上转为对super.h.invoke方法的调用,父类中的h即为我们在构造方法中传入的InvocationHandler对象,以下的toString()、buy()、hashCode()等方法同理
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void buy() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
//这里完成Method对象的初始化(通过反射在运行时获得Method对象)
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("Sell").getMethod("sell", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
Retrofit的代理模式
基本使用:
(1).定义接口
public interface GitHubService {
@GET("users/{user}/repos")
Call> listRepos(@Path("user") String user);
}
(2).新建retrofit对象,然后产生一个接口对象,然后调用具体方法去完成请求。
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("https://api.github.com/")
.build();
GitHubService service = retrofit.create(GitHubService.class);
(3).由创建的GitHubService的向web服务器发出同步或异步HTTP请求
Call> repos = service.listRepos("octocat");
retrofit.create方法就是通过动态代理的方式传入一个接口,返回了一个对象
public T create(final Class service) {
validateServiceInterface(service);//1
return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class>[] { service },
new InvocationHandler() {
private final Platform platform = Platform.get();
private final Object[] emptyArgs = new Object[0];
@Override public @Nullable Object invoke(Object proxy, Method method,
@Nullable Object[] args) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(this, args);//2
}
if (platform.isDefaultMethod(method)) {
return platform.invokeDefaultMethod(method, service, proxy, args);//3
}
return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);//4
}
});
}
注释分析
*1.判断是否是一个接口
*2.判断调用的方法是否属于Object,如toString,hashCode,属于Object执行
*3.判断平台是java还是Android,Android不会进
*4.解析接口的方法、注解、参数并执行方法(这里是Retrofit重点方法,代码细节超出本文主题范围,将在Retrofit源码分析重点探讨)
Retrofit为什么要这样设计?
个人认为主要有两点
*1.应用的请求接口可能有很多个,通过代理模式,能动态的为每个接口生成具体的代理类,并实现了我们的接口,我们不需要关心具体的细节,我们只需要声明接口然后传递给Retrofit即可,然后有Retrofit动态生成具体请求对象,发起请求并把结果返回给我们。
*2.让开发者与Retrofit的底层实现okhttp完全解耦,哪怕作者后期决定底层实现不再采用okhttp,开发者的代码依然不需要改变