简介
代理模式(Proxy Pattern)也称为委托模式,一般就是自己直接访问某个对象有一定困难,需要委托别人(代理对象)来间接访问的一种设计模式。代理模式其实在日常生活中很常见,比如你发现国外的名牌比较便宜,但是自己又不想花钱出国,于是你委托你国外的朋友帮你把名牌买回来。又或者比如你的老板不给你发工资,你得请一个律师帮你打官司。还有比如你想租房,你通过中介租房子,这也是代理。
代码实现
下面我们通过代码来实现租客(Tenant)通过中介(TenantProxy)租房的例子。
- 代码的最终目的是要通过中介(TenantProxy)把房子租下来,那么代码里面肯定要调用 TenantProxy 实例的 rent() 方法,代码如下:
java
public class Test {
public static void main(String[] args) {
TenantProxy tenantProxy = new TenantProxy();
tenantProxy.rent();
}
}
public class TenantProxy {
public void rent(){
System.out.println("租房子");
}
}
- 但是真实的情况是 Tenant 想租房,不是 TenantProxy 想租, 对 TenantProxy 中的代码进行改造后代码如下:
java
public class TenantProxy {
private Tenant tenant;
public TenantProxy(Tenant tenant){
this.tenant = tenant;
}
public void rent(){
tenant.rent();
}
}
public class Tenant {
public void rent(){
System.out.println("租房子");
}
}
public class Test {
public static void main(String[] args) {
Tenant tenant = new Tenant();
TenantProxy tenantProxy = new TenantProxy(tenant);
tenantProxy.rent();
}
}
3.又因为 Tenant 和 TenantProxy 实现的是同一个操作(租房子),将这个操作抽象成接口或者抽象类,最终代码如下:
java
public interface ITenant {
void rent();
}
public class TenantProxy implements ITenant{
private ITenant tenant;
public TenantProxy(ITenant tenant){
this.tenant = tenant;
}
public void rent(){
tenant.rent();
}
}
public class Tenant implements ITenant{
public void rent(){
System.out.println("租房子");
}
}
public class Test {
public static void main(String[] args) {
Tenant tenant = new Tenant();
TenantProxy tenantProxy = new TenantProxy(tenant);
tenantProxy.rent();
}
}
类图如下:
动态代理
如果现在租客想要通过这个中介买房,这时候就需要在 ITenant 接口中添加买房的方法了:
java
public interface ITenant {
void rent();
void buy();
}
代理类也要跟着修改:
java
public class TenantProxy implements ITenant{
private ITenant tenant;
public TenantProxy(ITenant tenant){
this.tenant = tenant;
}
public void rent(){
tenant.rent();
}
public void buy(){
tenant.buy();
}
}
随着业务变得越来越复杂,维护代理类和被代理类之间的关系也会变得很麻烦,这时候动态代理应运而生。使用动态代理的好处是可以通过一个代理类来代理所有的被代理类,动态代理不需要提前创建代理类,它会在代码运行时创建代理类的实例。
下面我们使用动态代理让租户可以通过中介租房和买房,代码如下:
java
public interface ITenant {
void rent();
void buy();
}
public class Tenant implements ITenant{
public void rent(){
System.out.println("动态代理租房子");
}
@Override
public void buy() {
System.out.println("动态代理买房子");
}
}
public class Test {
public static void main(String[] args) {
Tenant tenant = new Tenant();
ITenant iTenant = (ITenant) Proxy.newProxyInstance(
ITenant.class.getClassLoader(),
new Class<?>[] {ITenant.class},
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(tenant, args);
}
});
iTenant.rent();
iTenant.buy();
}
}
可以看到使用动态代理后,代理类 TenantProxy 直接不用写了,但是需要调用 java.lang.reflect.Proxy 的 newProxyInstance() 方法。
源码分析
来看看 Proxy 的 newProxyInstance() 方法做了什么(本文源码基于 Android API 33):
java
public class Proxy implements java.io.Serializable {
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
...
final Class<?>[] intfs = interfaces.clone();
...
// 1. 通过 loader 和 intfs 创建代理类
Class<?> cl = getProxyClass0(loader, intfs);
...
// 2. 获取代理类的构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
...
// 3. 创建代理类的实例返回
return cons.newInstance(new Object[]{h});
...
}
可以看到这里主要目的就是创建代理类的实例并返回,这样就可以调用代理类的实例的 rent() 了。
getProxyClass0() 方法的代码如下:
java
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
...
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
从这里的注释可以看到:如果实现该接口的代理类已经通过 loader 加载过,这个方法会直接返回缓存的副本;否则,将通过 ProxyClassFactory 来创建代理类。
proxyClassCache 是一个定义在 Proxy 中的数据结构:
java
public class Proxy implements java.io.Serializable {
// 用来缓存代理类
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
}
proxyClassCache 是 WeakCache<ClassLoader, Class<?>[], Class<?>> 类型的,其 get() 方法如下:
java
final class WeakCache<K, P, V> {
public V get(K key, P parameter) {
...
// 这里 subKeyFactory 即 new ProxyClassFactory(),
// subKeyFactory.apply() 会调用 ProxyClassFactory 实例的 apply() 方法
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
...
}
}
这里的 get() 方法中会调用 ProxyClassFactory 的 apply() 方法:
java
// 工厂类:通过 ClassLoader 和 接口组成的数组 来创建代理类的
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// 生成的代理 Class 的前缀
private static final String proxyClassNamePrefix = "$Proxy";
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
...
// 代理 Class 名由 proxyPkg + proxyClassNamePrefix + num 拼接而成
String proxyName = proxyPkg + proxyClassNamePrefix + num;
return generateProxy(proxyName, interfaces, loader, methodsArray,
exceptionsArray);
}
}
}
generateProxy() 是一个 native 方法
java
private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
ClassLoader loader, Method[] methods,
Class<?>[][] exceptions);
在 Android 中最终调用了 native 方法来生成代理类,由于动态代理是在运行时生成的,生成的代理类仅存在于内存中,并不会生成 .class 文件,因此你无法在 Android Studio 中找到。
在静态代理中,调用代理类的 rent() 方法会调用被代理类的 rent() 方法,在动态代理中,我们看到通过 Proxy 的 newProxyInstance() 方法创建了代理类的实例,但是里面是怎么把代理类的 rent() 方法传递给被代理类的 rent() 方法的呢?还是得通过生成的代理类的的代码来分析才行。
Java 中有生成代理类的 .class 文件的方法,调用 ProxyGenerator 的 generateProxyClass() 方法即可,但是最近发现这个方法在 Java 中不开放了,使用 ProxyGenerator 会直接报错说找不到,可能是因为安全问题不让用了。
以前这个方法是可行的。在项目中新建一个 module ,类型选择 java library ,命名为 lib , 代码如下:
java
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import sun.misc.ProxyGenerator;
public class Test2 {
public static void main(String[] args){
String name = ITenant.class.getName() + "$Proxy0";
//生成代理指定接口的Class数据
byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{ITenant.class});
FileOutputStream fos;
try {
fos = new FileOutputStream("lib/" + name + ".class");
fos.write(bytes);
fos.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
运行后会在当前项目的 lib 模块下生成 .class 文件:ITenant$Proxy0.class,代码如下:
java
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
public final class ITenant$Proxy0 extends Proxy implements ITenant {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public ITenant$Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} 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 rent() 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);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.example.lib.ITenant").getMethod("rent");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
可以看到生成的代理类会继承 java.lang.reflect.Proxy 并实现 ITenant 接口,里面也会有一个 rent() 方法,这样当你调用代理类的 rent() 方法时,就会调用 super.h 的 invoke() 方法, 传入的参数 m3 是: Class.forName("com.example.lib.ITenant").getMethod("rent"),super.h 就是 InvocationHandler 接口,也就是调用 InvocationHandler 的 invoke() 方法,也就会调用 tenant 的 rent() 方法。
所以其实动态代理跟静态代理做的事情是一样的,最大的区别是动态代理会在运行时动态创建代理类的实例,一个动态代理类还可以代理多个被代理类,更加灵活。