Java代理模式-静态代理与动态代理

244 阅读5分钟

代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。通俗的来讲代理模式就是我们生活中常见的中介。 举个例子来说明:张三想买场演唱会门票,虽然他可以自己去抢,但是这确实太浪费时间和精力了,或者说根本抢不到。于是张三就通过黄牛来买,黄牛来帮张三,张三只是负责选择自己喜欢的的位置,然后付钱就可以了。

  • 目的:
  1. 通过引入代理对象的方式来间接访问目标对象,防止直接访问目标对象给系统带来的不必要
  2. 复杂;通过代理对象对原有的业务增强;
  • 代理模式一般会有三个角色:
  1. 抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口
  2. 真实角色:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑在此。
  3. 代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理! 而访问者不再访问真实角色,而是去访问代理角色。

静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。一般来说,被代理对象和代理对象是一对一的关系,当然一个代理对象对应多个被代理对象也是可以的。 静态代理,一对一则会出现时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题,一对多则代理对象会出现扩展能力差的问题。

动态代理

是指在使用时再创建代理类和实例

  • 优点
  1. 只需要1个动态代理类就可以解决创建多个静态代理的问题,避免重复、多余代码
  2. 更强的灵活性
  • 缺点
  1. 效率低,相比静态代理中 直接调用目标对象方法,动态代理则需要先通过Java反射机制 从而 间接调用目标对象方法。
  2. 应用场景局限,因为 Java 的单继承特性(每个代理类都继承了 Proxy 类),即只能针对接口 创建 代理类,不能针对类创建代理类。
  3. 在java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler接口、另一个则是 Proxy类,这个类和接口是实现我们动态代理所必须用到的。
  4. InvocationHandler接口是给动态代理类实现的,负责处理被代理对象的操作的,而Proxy是用来创建动态代理类实例对象的,因为只有得到了这个对象我们才能调用那些需要代理的方法。

动态代理实现原理

通过调试模式我们发现,动态代理里,代理类的类名是这样的:

{$Proxy@517}"com.kanshu.testlibrary.agent.AaFactory@2f410acf"

这个代理类为何是这个名字?它是如何执行被代理对象的相关方法呢?我们在java文件编译后的目录里其实找不到这个名为$Proxy0的class文件的。 观察Proxy.newProxyInstance方法,与创建对象有关的代码主要有:

public static Object newProxyInstance(ClassLoader var0, Class<?>[] var1, InvocationHandler var2) throws IllegalArgumentException {
    Objects.requireNonNull(var2);
    Class[] var3 = (Class[])var1.clone();
    SecurityManager var4 = System.getSecurityManager();
    if (var4 != null) {
        checkProxyAccess(Reflection.getCallerClass(), var0, var3);
    }

    // 获得代理类的class对象:
    Class var5 = getProxyClass0(var0, var3);

    try {
        if (var4 != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), var5);
        }

        // 获得代理类的构造器:
        final Constructor var6 = var5.getConstructor(constructorParams);
        if (!Modifier.isPublic(var5.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    var6.setAccessible(true);
                    return null;
                }
            });
        }

        // 创建代理类的实例
        return var6.newInstance(var2);
    } catch (InstantiationException | IllegalAccessException var8) {
        throw new InternalError(var8.toString(), var8);
    } catch (InvocationTargetException var9) {
        Throwable var7 = var9.getCause();
        if (var7 instanceof RuntimeException) {
            throw (RuntimeException)var7;
        } else {
            throw new InternalError(var7.toString(), var7);
        }
    } catch (NoSuchMethodException var10) {
        throw new InternalError(var10.toString(), var10);
    }
}

看来其中的关键点就是如何获得代理类的class对象,我们进入getProxyClass0方法,进而进入proxyClassCache.get方法,通过这个这个方法所在的类名,我们可以推测,JDK内部使用了某种机制缓存了我们的代理类的class对象,同时get方法接受的参数是被代理类的类加载器和类实现的的接口。

public V get(K var1, P var2) {
    Objects.requireNonNull(var2);
    this.expungeStaleEntries();
    Object var3 = WeakCache.CacheKey.valueOf(var1, this.refQueue);
    Object var4 = (ConcurrentMap)this.map.get(var3);
    if (var4 == null) {
        ConcurrentMap var5 = (ConcurrentMap)this.map.putIfAbsent(var3, var4 = new ConcurrentHashMap());
        if (var5 != null) {
            var4 = var5;
        }
    }

    // 在这个get方法中,除去和缓存相关的操作,同时用到了被代理类的类加载器和类实现的的接口这两个参数
    Object var9 = Objects.requireNonNull(this.subKeyFactory.apply(var1, var2));
    Object var6 = (Supplier)((ConcurrentMap)var4).get(var9);
    WeakCache.Factory var7 = null;

    while(true) {
        if (var6 != null) {
            Object var8 = ((Supplier)var6).get();
            if (var8 != null) {
                return var8;
            }
        }

        if (var7 == null) {
            var7 = new WeakCache.Factory(var1, var2, var9, (ConcurrentMap)var4);
        }

        if (var6 == null) {
            var6 = (Supplier)((ConcurrentMap)var4).putIfAbsent(var9, var7);
            if (var6 == null) {
                var6 = var7;
            }
        } else if (((ConcurrentMap)var4).replace(var9, var6, var7)) {
            var6 = var7;
        } else {
            var6 = (Supplier)((ConcurrentMap)var4).get(var9);
        }
    }
}

我们再进入这个apply(var1, var2)方法的实现

public Class<?> apply(ClassLoader var1, Class<?>[] var2) {
    IdentityHashMap var3 = new IdentityHashMap(var2.length);
    Class[] var4 = var2;
    int var5 = var2.length;

    for(int var6 = 0; var6 < var5; ++var6) {
        Class var7 = var4[var6];
        Class var8 = null;

        try {
            var8 = Class.forName(var7.getName(), false, var1);
        } catch (ClassNotFoundException var15) {
        }

        if (var8 != var7) {
            throw new IllegalArgumentException(var7 + " is not visible from class loader");
        }

        if (!var8.isInterface()) {
            throw new IllegalArgumentException(var8.getName() + " is not an interface");
        }

        if (var3.put(var8, Boolean.TRUE) != null) {
            throw new IllegalArgumentException("repeated interface: " + var8.getName());
        }
    }

    String var16 = null;
    byte var17 = 17;
    Class[] var18 = var2;
    int var20 = var2.length;

    for(int var21 = 0; var21 < var20; ++var21) {
        Class var9 = var18[var21];
        int var10 = var9.getModifiers();
        if (!Modifier.isPublic(var10)) {
            var17 = 16;
            String var11 = var9.getName();
            int var12 = var11.lastIndexOf(46);
            String var13 = var12 == -1 ? "" : var11.substring(0, var12 + 1);
            if (var16 == null) {
                var16 = var13;
            } else if (!var13.equals(var16)) {
                throw new IllegalArgumentException("non-public interfaces from different packages");
            }
        }
    }

    if (var16 == null) {
        var16 = "com.sun.proxy.";
    }

    long var19 = nextUniqueNumber.getAndIncrement();
    // var23 代理类的类名
    String var23 = var16 + "$Proxy" + var19;
    // 代理类的字节码 所以是个byte数组
    byte[] var22 = ProxyGenerator.generateProxyClass(var23, var2, var17);

    try {
        return Proxy.defineClass0(var1, var23, var22, 0, var22.length);
    } catch (ClassFormatError var14) {
        throw new IllegalArgumentException(var14.toString());
    }
}

最终生成代理类的class对象是defineClass0方法,但是这个方法是个native方法,所以我们不去也无法深究它,但是通过这个方法的参数我们可以明显看到它接收了上面所生成的byte数组。 而我们通过ProxyUtils,这个自己写的工具类,将这个byte数组写入文件,我们并反编译,我们将会看到


import com.kanshu.testlibrary.agent.AaFactory;
import java.lang.reflect.*;

// 我们还会看到其中实现了业务接口的方法
public final class $Proxy0 extends Proxy
    implements AaFactory
{

    public $Proxy0(InvocationHandler invocationhandler)
    {
        super(invocationhandler);
    }

    public final boolean equals(Object obj)
    {
        try
        {
            return ((Boolean)super.h.invoke(this, m1, new Object[] {
                obj
            })).booleanValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void notify()
    {
        try
        {
            super.h.invoke(this, m8, null);
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString()
    {
        try
        {
            return (String)super.h.invoke(this, m2, null);
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void saleMan(String s)
    {
        try
        {
            // 而h则来自派生类Proxy中
            // 这个h的实例来自哪里?不就是我们在创建代理类的实例时传入的吗?
            super.h.invoke(this, m3, new Object[] {
                s
            });
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void wait(long l)
        throws InterruptedException
    {
        try
        {
            super.h.invoke(this, m5, new Object[] {
                Long.valueOf(l)
            });
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void wait(long l, int i)
        throws InterruptedException
    {
        try
        {
            super.h.invoke(this, m4, new Object[] {
                Long.valueOf(l), Integer.valueOf(i)
            });
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final Class getClass()
    {
        try
        {
            return (Class)super.h.invoke(this, m7, null);
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void notifyAll()
    {
        try
        {
            super.h.invoke(this, m9, null);
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode()
    {
        try
        {
            return ((Integer)super.h.invoke(this, m0, null)).intValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final void wait()
        throws InterruptedException
    {
        try
        {
            super.h.invoke(this, m6, null);
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable)
        {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    private static Method m1;
    private static Method m8;
    private static Method m2;
    private static Method m3;
    private static Method m5;
    private static Method m4;
    private static Method m7;
    private static Method m9;
    private static Method m0;
    private static Method m6;

    static 
    {
        try
        {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                Class.forName("java.lang.Object")
            });
            m8 = Class.forName("com.kanshu.testlibrary.agent.AaFactory").getMethod("notify", new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            //  我们定义的方法
            m3 = Class.forName("com.kanshu.testlibrary.agent.AaFactory").getMethod("saleMan", new Class[] {
                Class.forName("java.lang.String")
            });
            m5 = Class.forName("com.kanshu.testlibrary.agent.AaFactory").getMethod("wait", new Class[] {
                Long.TYPE
            });
            m4 = Class.forName("com.kanshu.testlibrary.agent.AaFactory").getMethod("wait", new Class[] {
                Long.TYPE, Integer.TYPE
            });
            m7 = Class.forName("com.kanshu.testlibrary.agent.AaFactory").getMethod("getClass", new Class[0]);
            m9 = Class.forName("com.kanshu.testlibrary.agent.AaFactory").getMethod("notifyAll", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
            m6 = Class.forName("com.kanshu.testlibrary.agent.AaFactory").getMethod("wait", new Class[0]);
        }
        catch(NoSuchMethodException nosuchmethodexception)
        {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch(ClassNotFoundException classnotfoundexception)
        {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

使用了动态代理的Retrofit Retrofit简单的说就是一个网络请求的适配器,它将一个基本的Java接口通过动态代理的方式翻译成一个HTTP请求,并通过OkHttp去发送请求。此外它还具有强大的可扩展性,支持各种格式转换以及RxJava。我们基于Retrofit2解析。 先定义一个名为X的java接口,当然里面有各种注解。 @FormUrlEncoded注解表示from表单,另外还有@Multipart等注解。@POST表示post请求,此外还可以使用@GET请求

public interface XService {
    @FormUrlEncoded
    @POST("user/bonuspools/userinfo")
    Observable<BaseResult<UserResult>> getUserInfo(@FieldMap Map<String, String> map);
}

然后如何使用的呢?

首先将域名传入构造一个Retrofit,然后通过retrofit中的create方法传入一个Java接口并得到一个x(当然x这个对象是经过处理了的)调用getPersonalListInfo(12)然后返回一个Call,最后这个Call调用了enqueue方法去异步请求http,这就是一个基本的Retrofit的网络请求。Retrofit2中Call接口的默认实现是OkHttpCall,它默认使用OkHttp3作为底层http请求client。

RetrofitHelper.getInstance()
    .createService(XService.class)
    .getUserInfo(map)
    .compose(asyncRequestData())
    .subscribe(data -> {
        // 数据请求成功
        }, error -> {
        // 数据请求失败
        });

我们只定义了一个接口XService,并没有实现这个接口,那么它是如何工作的呢?我们看看create方法的实现。 create()方法是个泛型方法,调用它时会返回一个范型T的对象,我们这里类型是XService接口,在内部实现上,很明显了使用了动态代理返回了一个XService的代理类。当调用XService内部方法的时候,会调用invoke方法。invoke方法内则通过内部一系列的封装最后返回一个Call对象。

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.adapt(okHttpCall);
          }
        });
}