美文网首页
利用java中的注解,反射,动态代理实现Retrofit

利用java中的注解,反射,动态代理实现Retrofit

作者: 被虐的小鸡 | 来源:发表于2020-07-20 11:12 被阅读0次

    静态代理

    静态代理我们都知道只能代理某一个接口,我记得之前有一个项目中使用的是httpClient,但是Google后来将httpclient移除了,我们后来使用了volley,但是发现需要修改的地方太多了,后来使用静态代理写了一个中间层,在代码中不直接调用具体的请求网络框架,而是使用中间代理层,这样以后换网络框架的时候只需要让新的框架实现请求网络接口就好了。

    Retrofit的用法

    Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("xxx")
        .build();
    
    XXService service = retrofit.create(XXService.class);
    
    interface XXService {
        @GET("xx/xxx")
        public Call getXX(@Query("xx") String xx);
    
        @POST("xx/xxx")
        public Call postXX(@Field("xx") String xx);
    }
    

    定义注解

    注解的生命周期
    1.SOURCE 用来对代码进行检测,添加辅助类。
    2.CLASS 字节码增强,修改字节码代码。
    3.RUNTIME 反射,在运行时赋值等。
    1.定一个GET,POST注解

    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface GET{
          String value();
    }
    @Retention(RetentionPolicy.RUNTIME)
    @Target(ElementType.METHOD)
    public @interface POST{
          String value();
    }
    

    2.定义Field,Query注解

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

    定义Retrofit类

    我们在使用的时候是用建造者模式来生成Retrofit对象的,建造者模式可以在build方法中对设置的数据进行判断,并且设置了baseurl和callAdapterFactory等

    public class Retrofit{
    
        static class Builder{
            private HttpUrl baseUrl;
            private Call.Factory callFactory;
    
            public Builder baseUrl(String url){
                this.baseUrl=HttpUrl.get(url);
                return this;
            }
    
            public Builder callFactory(Call.Factory factory){
                this.callFactory=factory;
    
                return this;
            }
    
            public Retrofit build(){
                if (baseUrl==null){
                    throw new IllegalArgumentException("baseurl can not null");
                }
    
                if (callFactory==null){
                    callFactory=new OkHttpClient();
                }
    
                return new Retrofit(baseUrl,callFactory);
            }
        }
    }
    

    在Retrofit中创建create方法

    通过create方法生成接口的代理对象,当调用接口中的某一个方法的时候对该方法上的注解进行解析
    在解析的过程中我们为了保证不是每次调用都需要解析,所以我们使用map集合将解析的数据都保存下来

    public <T> T create(Class<T> clazz) {
            return (T) Proxy.newProxyInstance(this.getClass().getClassLoader(), new Class[]{clazz}, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args){
                    ServiceMethod serviceMethod=loadServiceMethod(method);
                    return serviceMethod.invoke(args);
                }
            });
        }
    private ServiceMethod loadServiceMethod(Method method) {
            ServiceMethod result = serviceMethodCache.get(method);
            if (result!=null)
                return result;
            //A线程如果还没有将数据put待集合中,B线程也会走到这里,所以在锁里面需要再判断一次
            synchronized (serviceMethodCache){
                result=serviceMethodCache.get(method);
                if (result==null){
                    result=new ServiceMethod.Builder(this,method).build();
                    serviceMethodCache.put(method,result);
                }
            }
            return result;
        }
    

    ServiceMethod

    在ServiceMethod中来解析并存放数据,在调用invoke方法的时候使用okhttp进行网络请求

    public class ServiceMethod {
    
        private final String requestMethod;
        private final boolean hasBody;
        private final ParameterHandler[] parameterHandlers;
        private final String releativeUrl;
        private final HttpUrl baseUrl;
        private final Call.Factory callFactory;
        private FormBody.Builder formBuild;
        private HttpUrl.Builder urlBuilder;
    
        public ServiceMethod(Builder builder) {
            requestMethod = builder.requestMethod;
    
            hasBody = builder.hasBody;
    
            parameterHandlers = builder.parameterHandlers;
    
            releativeUrl = builder.releativeUrl;
    
            baseUrl = builder.retrofit.baseUrl;
    
            callFactory = builder.retrofit.callFactory;
    
            if (hasBody){
                formBuild = new FormBody.Builder();
            }
        }
    
        public Object invoke(Object[] args) {
            for (int i = 0; i < parameterHandlers.length; i++) {
                ParameterHandler parameterHandler = parameterHandlers[i];
    
                parameterHandler.apply(this,args[i].toString());
            }
    
            if (urlBuilder==null){
                urlBuilder=baseUrl.newBuilder(releativeUrl);
            }
    
            HttpUrl httpUrl = urlBuilder.build();
    
            FormBody build = null;
            if (formBuild!=null){
                build = formBuild.build();
            }
    
            Request request = new Request.Builder().url(httpUrl).method(requestMethod, build).build();
    
            return callFactory.newCall(request);
        }
    
        public void addQueryParameter(String key,String value) {
            if (urlBuilder==null){
                urlBuilder=baseUrl.newBuilder(releativeUrl);
            }
            urlBuilder.addQueryParameter(key,value);
        }
    
        public void addFieldParameter(String key,String value) {
            formBuild.add(key,value);
        }
    
        public static class Builder{
            private final Retrofit retrofit;
            private final Annotation[] methodAnnotations;
            private final Annotation[][] parameterAnnotations;
            private String requestMethod;
            private String releativeUrl;
            private boolean hasBody;
            private ParameterHandler[] parameterHandlers;
    
            public Builder(Retrofit retrofit, Method method) {
    
                this.retrofit=retrofit;
    
    
                methodAnnotations = method.getAnnotations();
    
                parameterAnnotations = method.getParameterAnnotations();
    
            }
    
            public ServiceMethod build(){
                for (Annotation methodAnnotation :
                        methodAnnotations) {
                    if (methodAnnotation instanceof POST){
                        this.releativeUrl = ((POST) methodAnnotation).value();
                        this.requestMethod="POST";
                        this.hasBody=true;
                    }else if (methodAnnotation instanceof GET){
                        this.releativeUrl = ((GET) methodAnnotation).value();
                        this.requestMethod="GET";
                        this.hasBody=false;
                    }
                }
    
                int length = parameterAnnotations.length;
    
                parameterHandlers = new ParameterHandler[length];
                for (int i = 0; i < length; i++) {
                    Annotation[] parameterAnnotation = parameterAnnotations[i];
                    for (Annotation annotation :
                            parameterAnnotation) {
                        if (annotation instanceof Query) {
                            String value = ((Query) annotation).value();
                            if (requestMethod.equals("POST")){
                                throw new IllegalArgumentException("post request can not use query");
                            }
                            parameterHandlers[i]=new ParameterHandler.QueryParameterHandler(value);
                        }else if (annotation instanceof Field){
                            String value = ((Field) annotation).value();
                            if (requestMethod.equals("GET")){
                                throw new IllegalArgumentException("get request can not use field");
                            }
    
                            parameterHandlers[i]=new ParameterHandler.FieldParameterHandler(value);
                        }
                    }
                }
    
    
                return new ServiceMethod(this);
            }
        }
    }
    

    ParameterHandler

    在这个类里面存储参数的键值对

    public abstract class ParameterHandler{
        public abstract void apply(ServiceMethod serviceMethod,String value);
    
    
        static class QueryParameterHandler extends ParameterHandler{
            private final String key;
    
            public QueryParameterHandler(String value) {
                this.key=value;
            }
    
            @Override
            public void apply(ServiceMethod serviceMethod, String value) {
                serviceMethod.addQueryParameter(key,value);
            }
        }
    
        static class FieldParameterHandler extends ParameterHandler{
            private final String key;
    
            public FieldParameterHandler(String value) {
                this.key=value;
            }
    
            @Override
            public void apply(ServiceMethod serviceMethod, String value) {
                serviceMethod.addFieldParameter(key,value);
            }
        }
    }
    
    

    相关文章

      网友评论

          本文标题:利用java中的注解,反射,动态代理实现Retrofit

          本文链接:https://www.haomeiwen.com/subject/hajekktx.html