Java基础补完系列之动态代理

动态代理的基本使用

动态代理是Java的一种基本技术手段,他能在运行期对一个对象的一些方法做一些修改,而得到一个新的对象,而不需要预先定义好对应的对象的class,下面我们先来看一下代理模式的简单使用方式:

先定义好一个接口:

public interface IPrint {
    void print();
}

然后在定义这个接口的实现类:

public class Person implements IPrint {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public void print() {
        System.out.println(toString());
    }
}

这是一个简单的实体类,并实现了接口IPrint,我们之后代理的就是这个类的实例。

然后定义代理相关的核心类:

public class LogHandler<T> implements InvocationHandler {

    private T targetObject;

    public LogHandler(T targetObject) {
        this.targetObject = targetObject;
    }

    public T newProxyInstance(T targetObject) {
        return (T) Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
                targetObject.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
        System.out.println("invoke start");
        Object result = method.invoke(targetObject, objects);
        System.out.println("invoke end");
        return result;
    }
}

在这个类里,我们发现了一个我们不认识的方法:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

他的源码解析我们放在下一章去解读,我们现在只需要知道,它的返回值是一个实现了传入的interfaces接口列表的每一个接口的类的实例,而这个代理类的每一个方法的实现均依赖于传入的h的invoke方法的具体实现,当代理类一个方法被调用的时候,相当于调用到h的invoke方法,而h的invoke方法一般会调用method.invoke(targetObject, objects),这相当于是在调用被代理对象的方法。

我们来看具体使用代理的方式:

Person tony = new Person("Tony", 1);
LogHandler<IPrint> logHandler = new LogHandler<>(tony);
IPrint iPrint =  logHandler.newProxyInstance(tony);
iPrint.print();
iPrint.toString();

invoke start
Person{name='Tony', age=1}
invoke end
invoke start
invoke end

可以看到我们通过动态代理得到的对象是实现了IPrint接口的,而接口里面的方法print被代理了,在调用的时候除了原始类本身的打印外,还有在代理实现的打印,而且代理类的继承于Object的方法也被代理了。

我们能够通过打印代理Class的参数列表等来看其的类的样子:

public final class com.sun.proxy.$Proxy0 extends java.lang.reflect.Proxy
{
 public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler);

  public final boolean equals(java.lang.Object);
  public final java.lang.String toString();
  public final int hashCode();
  public final void print(){
      h.invoke(this,m0, Object[] objects)
  }

  java.lang.reflect.Method m1;
  java.lang.reflect.Method m3;
  java.lang.reflect.Method m2;
  java.lang.reflect.Method m0;
}

其中print的实现为猜想的结果。

动态代理的源码解读

我们学习了动态代理的基本使用之后,我们再来看看动态代理的源码:

先从

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

说起,他的实现如下(Android上的实现):

public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
{
    Objects.requireNonNull(h);

    final Class<?>[] intfs = interfaces.clone();
    // Android-removed: SecurityManager calls
    /*
    final SecurityManager sm = System.getSecurityManager();
    if (sm != null) {
        checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
    }
    */

    /*
     * Look up or generate the designated proxy class.
     */
     //获取代理类的Class对象
    Class<?> cl = getProxyClass0(loader, intfs);

    /*
     * Invoke its constructor with the designated invocation handler.
     */
    try {
        // Android-removed: SecurityManager / permission checks.
        /*
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }
        */

        final Constructor<?> cons = cl.getConstructor(constructorParams);
        final InvocationHandler ih = h;
        if (!Modifier.isPublic(cl.getModifiers())) {
            // BEGIN Android-changed: Excluded AccessController.doPrivileged call.
            /*
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    cons.setAccessible(true);
                    return null;
                }
            });
            */

            cons.setAccessible(true);
            // END Android-removed: Excluded AccessController.doPrivileged call.
        }
        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);
    }
}

他的逻辑很简单,就是通过传来的接口信息以及类加载器创建出代理类的Class对象,然后调用构造方法来创建出具体的对象,所以最关键的是,他创建出来的Class对象具体是啥样的,我们来具体看这个方法:

private static Class<?> getProxyClass0(ClassLoader loader,
                                       Class<?>... interfaces) {
    if (interfaces.length > 65535) {
        throw new IllegalArgumentException("interface limit exceeded");
    }

    // 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);
}

他是通过调用WeakCache.get方法来得到的,而WeakCache类主要是做一些缓冲的工作的,他最终会掉到ProxyClassFactory的apply方法:

public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

    Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
        for (Class<?> intf : interfaces) {
            /*
             * Verify that the class loader resolves the name of this
             * interface to the same Class object.
             */
            Class<?> interfaceClass = null;
            try {
                interfaceClass = Class.forName(intf.getName(), false, loader);
            } catch (ClassNotFoundException e) {
            }
            if (interfaceClass != intf) {
                throw new IllegalArgumentException(
                    intf + " is not visible from class loader");
            }
            /*
             * Verify that the Class object actually represents an
             * interface.
             */
            if (!interfaceClass.isInterface()) {
                throw new IllegalArgumentException(
                    interfaceClass.getName() + " is not an interface");
            }
            /*
             * Verify that this interface is not a duplicate.
             */
            if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                throw new IllegalArgumentException(
                    "repeated interface: " + interfaceClass.getName());
            }
        }
    
        String proxyPkg = null;     // package to define proxy class in
        int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
    
        /*
         * Record the package of a non-public proxy interface so that the
         * proxy class will be defined in the same package.  Verify that
         * all non-public proxy interfaces are in the same package.
         */
        for (Class<?> intf : interfaces) {
            int flags = intf.getModifiers();
            if (!Modifier.isPublic(flags)) {
                accessFlags = Modifier.FINAL;
                String name = intf.getName();
                int n = name.lastIndexOf('.');
                String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                if (proxyPkg == null) {
                    proxyPkg = pkg;
                } else if (!pkg.equals(proxyPkg)) {
                    throw new IllegalArgumentException(
                        "non-public interfaces from different packages");
                }
            }
        }
    
        if (proxyPkg == null) {
            // if no non-public proxy interfaces, use the default package.
            proxyPkg = "";
        }
    
        {
            // Android-changed: Generate the proxy directly instead of calling
            // through to ProxyGenerator.
            List<Method> methods = getMethods(interfaces);
            Collections.sort(methods, ORDER_BY_SIGNATURE_AND_SUBTYPE);
            validateReturnTypes(methods);
            List<Class<?>[]> exceptions = deduplicateAndGetExceptions(methods);
    
            Method[] methodsArray = methods.toArray(new Method[methods.size()]);
            Class<?>[][] exceptionsArray = exceptions.toArray(new Class<?>[exceptions.size()][]);
    
            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
            return generateProxy(proxyName, interfaces, loader, methodsArray,
                                 exceptionsArray);
        }
    }
}

他分如下步骤:

  1. 做一些检验工作,分别是接口能否被类加载器加载,Class数组是不是都是接口,接口是否有重复,接口如果我们传入的类加载器和接口数组是使用具体的对象拿到的,这些校验肯定是能通过的;
  2. 然后获取代理类的包名,如果所有接口均是public的,代理类包名就为空,否则就是非public的接口的包名(如果有多个非public的接口,且包名不同,会抛出异常,当然我们代码不可能实现多个包下的非public的接口);
  3. 获取所有接口的方法列表,并检查是否有相同方法名以及参数列表,但是返回值不同的方法,有就抛出异常;
  4. 获取方法返回的异常列表;
  5. 然后调用native方法具体创建代理类对象。

以上做的这些检查,如果我们的类加载器和接口列表是通过targetObject.getClass().getClassLoader(),targetObject.getClass().getInterfaces()来取得的,那些检查是一定能通过的。

获取方法列表具体如下,它除了接口里面的方法外,还会添加equals、hashCode、toString三个方法,每个接口还会递归获取其父类的方法。

private static List<Method> getMethods(Class<?>[] interfaces) {
    List<Method> result = new ArrayList<Method>();
    try {
        result.add(Object.class.getMethod("equals", Object.class));
        result.add(Object.class.getMethod("hashCode", EmptyArray.CLASS));
        result.add(Object.class.getMethod("toString", EmptyArray.CLASS));
    } catch (NoSuchMethodException e) {
        throw new AssertionError();
    }

    getMethodsRecursive(interfaces, result);
    return result;
}

private static void getMethodsRecursive(Class<?>[] interfaces, List<Method> methods) {
    for (Class<?> i : interfaces) {
        getMethodsRecursive(i.getInterfaces(), methods);
        Collections.addAll(methods, i.getDeclaredMethods());
    }
}

创建Class对象的native方法如下:

private static native Class<?> generateProxy(String name, Class<?>[] interfaces,
                                                 ClassLoader loader, Method[] methods,
                                                 Class<?>[][] exceptions);

由于并没有代理类所对应的class文件,所以常规的创建Class方法行不通,这儿是通过native方法来实现的。

至此我们就解读完了代理类的创建过程。

我们从源码中能总结出如下几点:

  1. 动态代理只能够实现代理对象接口上的方法;
  2. 通过动态代理后,被代理对象的信息是不会出现在代理类的Class里面的,也就是说动态代理其实是正对接口列表来获取代理类,要与被代理对象关联,只能通过InvocationHandler.invoke来实现。

最后我们简单的站在动态代理的角度解读下Retrofit的创建使用过程。

相信使用过Retrofit的人对如下代码很熟悉:

api = Retrofit.Builder()
    .baseUrl(BASE_URL)
    .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
    .addConverterFactory(GsonConverterFactory.create())
    .client(builder.build())
    .build()
    .create(Api::class.java)

他在最后一步,通过调用Retrofit的create方法,传入的是一个接口的Class对象,最后得到的是一个实例,我们能很简单的猜到它内部使用了动态代理来实现的。他的集体实现如下:

  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, 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 serviceMethod = loadServiceMethod(method);
            OkHttpCall okHttpCall = new OkHttpCall<>(serviceMethod, args);
            return serviceMethod.callAdapter.adapt(okHttpCall);
          }
        });
  }

可以看出他的实例就是通过动态代理来创建的。

在invoke方法里,先判断当前调用的是接口定义的方法还是属于Object的方法,Object的方法就直接调用,如果是接口定义的方法,就解析方法的注解信息,存到serviceMethod,然后调用请求接口。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值