从Retrofit初探动态代理及注解.

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/heming9174/article/details/88357299

今天上午刚刚写完Retrofit的源码解读.
写完以后思考了一下,发现了2个问题.
1.对于Proxy.newProxyInstance()这个动态代理还是一知半解.
2.Retrofit是如何将InterfaceService传入的值 传递给OkHttpCall进行网路网络请求的.
怀着这2个疑问,再学习研究一番.

动态代理(Proxy.newProxyInstance())

对于动态代理的概念性的了解和相关的,可以参考一下Retrofit那篇,引用的动态代理博文.

在这里插入图片描述
对于Retrofit,我们都知道核心是这个,能够将InterfaceService class类通过动态代理获取到InterfaceService对象的这个过程.

点开Proxy.newProxyInstance()的源码,了解一下.
在这里插入图片描述
发现核心是这return的3个方法.
熟悉反射的一样就看出来了,这
getConstructor()和newInstance() 不就是反射创建对象么.
重点看一下getProxyClass这个方法.
这个方法传入的是对应的classLoader和这个InterfaceService.class
在这里插入图片描述
根据传入的参数关键字interfaces(即InterfaceService.class),查找它是如何被动态代理的.

通过观察这个源码,我们发现了几个信息.

  1. loader.proxyCache似乎和Retrofit的serviceMethodCache起的作用一样,都是一个Map结构的缓存.
    当已经存在时,直接通过缓存获取.不存在再进行创建,然后添加到loader.proxyCache内.
  2. 动态代理的类是以 包名+ .$Proxy + 0(从0开始自增).来命名的.
    在这里插入图片描述
  3. 最终创建动态代理类的方法是
Class <?> result = generateProxy(name, interfaces, loader, methodsArray, exceptionsArray);

研究一下这个"generateProxy()".
在这里插入图片描述
发现已经是个Native方法了.
GG,不过总体来说,我们已经知道了InterfaceService的动态代理类是通过底层C来实现的,然后通过反射生成对应的接口对象.

验证.

源码里面看到的终究是猜测,实际上,还是得验证一下才行.
同时也学习一下如何真正的使用动态代理和获取注解信息.

主方法:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        final User userProxy = getProxy(User.class);
        //验证一下动态的代理生成的类名
        System.out.println("尝试验证一下动态代理类的类名:" + userProxy.getClass().getName());

        //手动点击,获取Annotation
        findViewById(R.id.clickNow).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if (userProxy != null) {
                    userProxy.getUser("试试看啊");
                }
            }
        });
    }

    @SuppressWarnings("unchecked")
    private <T> T getProxy(Class<T> service) {
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] objects) throws Throwable {
                        getAnnotation(method);
                        System.out.println("尝试获取Parms注解填入的值:" + objects[0]);
                        return null;
                    }
                });
    }

    /**
     * 模仿Retrofit获取注解信息和参数注解的值.
     */
    private void getAnnotation(Method method) {
        //分别获取方法的注解名,属性注解类型,属性注解名
        Annotation[] annotations = method.getAnnotations();
        Type[] parmsTypeAnnotations = method.getGenericParameterTypes();
        Annotation[][] parmsAnnotations = method.getParameterAnnotations();

        if (annotations.length > 0) {
            System.out.println("尝试获取Test注解信息:" + annotations[0]);
        }

        if (parmsTypeAnnotations.length > 0 && parmsAnnotations.length > 0) {
            System.out.println("尝试获取Parms注解名称:" + parmsTypeAnnotations[0]);
            System.out.println("尝试获取Parms注解内容的key:" + parmsAnnotations[0][0]);
        }
    }
}

InterfaceService:

public interface User {
    @Test
    void getUser(@Parms("okok") String ojbk);
}

自定义的2个注解.

@Retention(RetentionPolicy.RUNTIME)
public @interface Test {
}

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@interface Parms {
    String value();
}

通过这个简单的类…
动态代理获取User的接口对象 --> 调用getUser() --> 触发InvocationHandler的invoke() --> 获取注解信息.
相关注释已经尽量写了.
在这里插入图片描述

总结.

  1. 获取的动态代理类名不对啊.
    不符合我的预期,不是 包名 + $Proxy + 0,而是 “” + $Proxy + 0.
    拐回去看源码.发现:

在这里插入图片描述
只能把原因归结于InterfaceService.getPackageName$()获取失败了,但是这个方法没有办法继续追踪了.
以后有机会再找找原因.

  1. 获取属性注解对应的值.
    发现原来就是InvocationHandler的invoke()方法里面的Object [] args.
    这个数组的值就是InterfaceService方法传入的值.

这也就解释了,Retrofit是如何将InterfaceService的方法值传递给OkHttpCall去执行网络请求的.
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

又是收获满满的一天…
咋感觉提出离职,主动走出舒适区后,这种紧迫感下每天的收获比之前多多了.

猜你喜欢

转载自blog.csdn.net/heming9174/article/details/88357299