Retrofit中的注解、反射與代理模式

Retrofit 是一個(gè) RESTful 的 HTTP 網(wǎng)絡(luò)請(qǐng)求框架的封裝,網(wǎng)絡(luò)請(qǐng)求的工作本質(zhì)上是 OkHttp 完成,而 Retrofit 僅負(fù)責(zé)網(wǎng)絡(luò)請(qǐng)求接口的封裝,其內(nèi)部實(shí)現(xiàn)實(shí)際上是使用了代理模式,為了更好的學(xué)習(xí)Retrofit框架,我們先從代理模式開始。

代理模式

代理模式,就是為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)。如果在直接訪問(wèn)對(duì)象時(shí)帶來(lái)的問(wèn)題,比如說(shuō):要訪問(wèn)的對(duì)象在遠(yuǎn)程的機(jī)器上。在面向?qū)ο笙到y(tǒng)中,有些對(duì)象由于某些原因(比如對(duì)象創(chuàng)建開銷很大,或者某些操作需要安全控制,或者需要進(jìn)程外的訪問(wèn)),直接訪問(wèn)會(huì)給使用者或者系統(tǒng)結(jié)構(gòu)帶來(lái)很多麻煩,我們可以在訪問(wèn)此對(duì)象時(shí)加上一個(gè)對(duì)此對(duì)象的訪問(wèn)層。

代理模式的使用

來(lái)看一個(gè)代理模式的例子。

定義接口:

/**
 * 代理抽象角色: 定義了服務(wù)的接口
 * 代理角色和真實(shí)角色對(duì)外提供的公共方法,一般為一個(gè)接口
 */
public interface Sing{
  public void sing();
}

實(shí)現(xiàn)接口:

/**
 *  實(shí)現(xiàn)類: 提供唱歌服務(wù)的Jack
 *  定義了真是角色所要實(shí)現(xiàn)的業(yè)務(wù)邏輯
 */
 public class Jack implements Sing{
   @Override
   public void sing(){
     System.out.println("努力唱歌")
   }
 }

代理對(duì)象:

/**
 * 代理對(duì)象:演唱代理人
 * 是真實(shí)角色的代理,通過(guò)真實(shí)角色的業(yè)務(wù)邏輯方法來(lái)實(shí)現(xiàn)抽象方法,并可以附加自己的操作
 * 將統(tǒng)一的流程控制都放到代理角色中處理。
 */
public class SingAgent implements Sing {

    private final Sing sing;

    public Agent(Sing Sing) {
        this.Sing = sing;
    }

    //....前置處理
    public void before() {
        System.out.println("準(zhǔn)備");
    }

    //....后置處理
    public void after() {
        System.out.println("打分");
    }

    @Override
    public void massage() {
        before();
        Sing.sing();
        after();
    }
}

代理模式使用:

public class Main {

    public static void main(String[] args) throws Exception {
        //靜態(tài)代理
       Sing sing = new Jack();
       SingAgent agent = new Agent(sing);  
       agent.sing();
      }
}

在靜態(tài)代理中,一個(gè)代理類可以代理多個(gè)真實(shí)對(duì)象,我們除了定義Jack之外,還可以定義David。如果我們的Jack除了會(huì)Sing(唱歌)之外,還會(huì)Dance(跳舞)。

public interface Dance{
  public void dance();
}

public class Jack implements Sing, Dance{
  ......
}

但是一個(gè)代理,只能實(shí)現(xiàn)一個(gè)抽象接口,為了能代理Jack對(duì)象,必須再創(chuàng)建一個(gè)DanceAgent,這樣一來(lái),代碼中就會(huì)有很多代理類。所以我們就必須想辦法,通過(guò)一個(gè)代理類,實(shí)現(xiàn)全部的代理功能,這時(shí)候就用到了動(dòng)態(tài)代理。


public static void main(String[] args) throws Exception {
    //靜態(tài)代理
   Jack jack = new Jack();

   Object o = Proxy.newProxyInstance(Main.class.getClassLoader(), //類加載器
           new Class[]{ Sing.class, Dance.class },  //代理的接口數(shù)組
           new InvocationHandler() {  //回調(diào)方法
               @Override
               public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
  //                        System.out.println(o.toString());
                   return method.invoke(jack, objects);
               }
           });
   Sing sing = (Sing) o;
   sing.sing();
  }

在這里,使用了Proxy類中的newProxyInstance方法,需要提供三個(gè)參數(shù),類加載器、代理的接口數(shù)組、回調(diào)方法。先創(chuàng)建了一個(gè)Jack對(duì)象,然后創(chuàng)建了一個(gè)動(dòng)態(tài)代理對(duì)象o,然后通過(guò)類型轉(zhuǎn)換,將o轉(zhuǎn)化成要代理的接口,調(diào)用sing()方法的時(shí)候,就會(huì)觸發(fā)InvocationHandler.invoke()這個(gè)回調(diào)。回調(diào)方法中也有三個(gè)參數(shù),o就是我們的動(dòng)態(tài)對(duì)理對(duì)象,method就是接口的方法,objects就是方法參數(shù)。然后調(diào)用method.invoke()真正實(shí)現(xiàn)jacksing()方法

所以,現(xiàn)在我們大概能了解到什么時(shí)候使用代理模式了吧。比如在早期網(wǎng)絡(luò)實(shí)現(xiàn)的時(shí)候,用的是Volley框架,如果對(duì)于每一條網(wǎng)絡(luò)請(qǐng)求,都是直接用Volley去建立,那么到了后面換成Okhttp框架的時(shí)候,就要對(duì)每一條網(wǎng)絡(luò)請(qǐng)求都去修改代碼,十分麻煩。而使用代理模式,則可以添加一個(gè)服務(wù)接口(定義了get和post),把Volley封裝,并創(chuàng)建代理類,在代理對(duì)象中調(diào)用封裝層的真實(shí)實(shí)現(xiàn)。后期切換框架的時(shí)候也更方便。

Proxy.newProxyInstance()原理

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

        final Class<?>[] intfs = interfaces.clone(); //拷貝接口
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs); //獲取Class對(duì)象

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams); //利用反射獲取class 的構(gòu)造方法
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});  //對(duì)class進(jìn)行實(shí)例化
        } 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);
        }
    }

newProxyInstance方法中,interfaces.clone()先對(duì)接口數(shù)組進(jìn)行了拷貝,然后利用getProxyClass0方法創(chuàng)建Class對(duì)象,然后通過(guò)反射,獲得它的構(gòu)造方法cons,最后利用cons.newInstance()創(chuàng)建該類的實(shí)例。

我們的類是怎么來(lái)的?我們先來(lái)回顧一下類的完整生命周期。

Java源文件(.java) —編譯—> Java字節(jié)碼(.class) —類加載—> Class對(duì)象 —實(shí)例化—> 實(shí)例對(duì)象 ———>卸載

而我們的.class文件一般都是一個(gè)實(shí)實(shí)在在的文件,是在硬盤中存在的。但是在動(dòng)態(tài)代理中,他這個(gè)Class對(duì)象,是在內(nèi)存中去生成的。我們來(lái)看getProxyClass0這個(gè)方法

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

        return proxyClassCache.get(loader, interfaces);
    }

Proxy里面有一個(gè)proxyClassCache對(duì)象,該對(duì)象是一個(gè)WeakCache實(shí)例,該緩存可以保存已經(jīng)生成過(guò)的代理類,如果有則直接返回。如果沒有的話,則通過(guò)ProxyClassFactory去創(chuàng)建代理類對(duì)象。

private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        ......
        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            ......

            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {

                throw new IllegalArgumentException(e.toString());
            }
        }
    }

ProxyClassFactory中,通過(guò)ProxyGenerator.generateProxyClass創(chuàng)建了代理類數(shù)據(jù),返回的是一個(gè)byte數(shù)組,然后通過(guò)defineClass0去解析這個(gè)byte數(shù)組,并生成一個(gè)代理類對(duì)象。我們可以把這個(gè)byte數(shù)組通過(guò)數(shù)據(jù)流的方式輸出到文件中。

private static void proxy() throws Exception {
       String name = Sing.class.getName() + "$Proxy0";
       //生成代理指定接口的Class數(shù)據(jù)
       byte[] bytes = ProxyGenerator.generateProxyClass(name, new Class[]{Sing.class});
       FileOutputStream fos = new FileOutputStream("lib/" + name + ".class");
       fos.write(bytes);
       fos.close();
   }

我們來(lái)觀察它的文件結(jié)構(gòu)。

public final class Sing$Proxy0 extends Proxy implements Sing {
    private static Method m3;

    //構(gòu)造方法接受InvocationHandler
    public Sing$Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }
    ......

    //在super中,也就是Proxy中,h就是傳入的InvocationHandler
    public final void sing() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    ......

    //靜態(tài)代碼塊中,利用反射,獲得sing()方法
    static {
            try {
                m3 = Class.forName("com.enjoy.lib.Sing").getMethod("sing");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
}

Retrofit的簡(jiǎn)單實(shí)現(xiàn)

在接下來(lái),我們將利用注解、反射與動(dòng)態(tài)代理,對(duì)Retrofit進(jìn)行一個(gè)簡(jiǎn)單的實(shí)現(xiàn)。在此之前,我們先來(lái)看一下Retrofit的簡(jiǎn)單使用。

Retrofit的基本使用

Api定義:
創(chuàng)建接api接口

//這里使用的是高德地圖提供的天氣api
public interface WeatherApi {

    @POST("/v3/weather/weatherInfo")
    @FormUrlEncoded
    Call<ResponseBody> postWeather(@Field("city") String city, @Field("key") String key);


    @GET("/v3/weather/weatherInfo")
    Call<ResponseBody> getWeather(@Query("city") String city, @Query("key") String key);
}

Retrofit使用:
創(chuàng)建Retrofit對(duì)象,然后利用create()創(chuàng)建了api接口的對(duì)象,最后調(diào)用api的方法。

Retrofit retrofit = new Retrofit.Builder().baseUrl("https://restapi.amap.com")
               .build();
weatherApi = retrofit.create(WeatherApi.class);
Call<ResponseBody> call = weatherApi.getWeather("110101", "ae6c53e2186f33bbf240a12d80672d1b");
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                if (response.isSuccessful()){
                    ResponseBody body = response.body();
                    try {
                        String string = body.string();
                        Log.i(TAG, "onResponse get: " + string);
                    } catch (IOException e) {
                        e.printStackTrace();
                    } finally {
                        body.close();
                    }
                }

            }
            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {

            }
        });

觀察上面代碼,Retrofit在創(chuàng)建的時(shí)候,使用的是構(gòu)建者模式,它可以將一個(gè)復(fù)雜對(duì)象的構(gòu)建和它的表示分離,可以讓使用者方便使用,不必知道內(nèi)部的細(xì)節(jié)。而在創(chuàng)建Api接口對(duì)象的時(shí)候, 使用的就是動(dòng)態(tài)代理。接下來(lái)我們來(lái)實(shí)現(xiàn)一個(gè)自己的Retrofit。

EnjoyRetrofit.java

public class EnjoyRetrofit {

    final Map<Method, ServiceMethod> serviceMethodCache = new ConcurrentHashMap<>();
    final Call.Factory callFactory;
    final HttpUrl baseUrl;

    EnjoyRetrofit(Call.Factory callFactory, HttpUrl baseUrl) {
        this.callFactory = callFactory;
        this.baseUrl = baseUrl;
    }

    public <T> T create(final Class<T> service) {
        return (T) Proxy.newProxyInstance(service.getClassLoader(), new Class[]{service},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //解析這個(gè)method 上所有的注解信息
                        ServiceMethod serviceMethod = loadServiceMethod(method);
                        //args:
                        return serviceMethod.invoke(args);
                    }
                });
    }

    private ServiceMethod loadServiceMethod(Method method) {
        //先不上鎖,避免synchronized的性能損失
        ServiceMethod result = serviceMethodCache.get(method);
        if (result != null) return result;
        //多線程下,避免重復(fù)解析,
        synchronized (serviceMethodCache) {
            result = serviceMethodCache.get(method);
            if (result == null) {
                result = new ServiceMethod.Builder(this, method).build();
                serviceMethodCache.put(method, result);
            }
        }
        return result;
    }


    /**
     * 構(gòu)建者模式,將一個(gè)復(fù)雜對(duì)象的構(gòu)建和它的表示分離,可以使使用者不必知道內(nèi)部組成的細(xì)節(jié)。
     */
    public static final class Builder {
        private HttpUrl baseUrl;
        //Okhttp->OkhttClient
        private okhttp3.Call.Factory callFactory;  //null


        public Builder callFactory(okhttp3.Call.Factory factory) {
            this.callFactory = factory;
            return this;
        }

        public Builder baseUrl(String baseUrl) {
            this.baseUrl = HttpUrl.get(baseUrl);
            return this;
        }

        public EnjoyRetrofit build() {
            if (baseUrl == null) {
                throw new IllegalStateException("Base URL required.");
            }
            okhttp3.Call.Factory callFactory = this.callFactory;
            if (callFactory == null) {
                callFactory = new OkHttpClient();
            }

            return new EnjoyRetrofit(callFactory, baseUrl);
        }
    }
}

ServiceMethod.java

/**
 * 記錄請(qǐng)求類型  參數(shù)  完整地址
 */
public class ServiceMethod {

    private final Call.Factory callFactory;
    private final String relativeUrl;
    private final boolean hasBody;
    private final ParameterHandler[] parameterHandler;
    private FormBody.Builder formBuild;
    HttpUrl baseUrl;
    String httpMethod;
    HttpUrl.Builder urlBuilder;

    public ServiceMethod(Builder builder) {
        baseUrl = builder.enjoyRetrofit.baseUrl;
        callFactory = builder.enjoyRetrofit.callFactory;

        httpMethod = builder.httpMethod;
        relativeUrl = builder.relativeUrl;
        hasBody = builder.hasBody;
        parameterHandler = builder.parameterHandler;

        //如果是有請(qǐng)求體,創(chuàng)建一個(gè)okhttp的請(qǐng)求體對(duì)象
        if (hasBody) {
            formBuild = new FormBody.Builder();
        }
    }

    public Object invoke(Object[] args) {
        /**
         * 1  處理請(qǐng)求的地址與參數(shù)
         */
        for (int i = 0; i < parameterHandler.length; i++) {
            ParameterHandler handlers = parameterHandler[i];
            //handler內(nèi)本來(lái)就記錄了key,現(xiàn)在給到對(duì)應(yīng)的value
            handlers.apply(this, args[i].toString());
        }

        //獲取最終請(qǐng)求地址
        HttpUrl url;
        if (urlBuilder == null) {
            urlBuilder = baseUrl.newBuilder(relativeUrl);
        }
        url = urlBuilder.build();

        //請(qǐng)求體
        FormBody formBody = null;
        if (formBuild != null) {
            formBody = formBuild.build();
        }

        Request request = new Request.Builder().url(url).method(httpMethod, formBody).build();
        return callFactory.newCall(request);
    }

    // get請(qǐng)求,  把 k-v 拼到url里面
    public void addQueryParameter(String key, String value) {
        if (urlBuilder == null) {
            urlBuilder = baseUrl.newBuilder(relativeUrl);
        }
        urlBuilder.addQueryParameter(key, value);
    }

    //Post   把k-v 放到 請(qǐng)求體中
    public void addFiledParameter(String key, String value) {
        formBuild.add(key, value);
    }


    public static class Builder {

        private final EnjoyRetrofit enjoyRetrofit;
        private final Annotation[] methodAnnotations;
        private final Annotation[][] parameterAnnotations;
        ParameterHandler[] parameterHandler;
        private String httpMethod;
        private String relativeUrl;
        private boolean hasBody;

        public Builder(EnjoyRetrofit enjoyRetrofit, Method method) {
            this.enjoyRetrofit = enjoyRetrofit;
            //獲取方法上的所有的注解
            methodAnnotations = method.getAnnotations();
            //獲得方法參數(shù)的所有的注解 (一個(gè)參數(shù)可以有多個(gè)注解,一個(gè)方法又會(huì)有多個(gè)參數(shù))
            parameterAnnotations = method.getParameterAnnotations();
        }

        public ServiceMethod build() {

            /**
             * 1 解析方法上的注解, 只處理POST與GET
             */
            for (Annotation methodAnnotation : methodAnnotations) {
                if (methodAnnotation instanceof POST) {
                    //記錄當(dāng)前請(qǐng)求方式
                    this.httpMethod = "POST";
                    //記錄請(qǐng)求url的path
                    this.relativeUrl = ((POST) methodAnnotation).value();
                    // 是否有請(qǐng)求體
                    this.hasBody = true;
                } else if (methodAnnotation instanceof GET) {
                    this.httpMethod = "GET";
                    this.relativeUrl = ((GET) methodAnnotation).value();
                    this.hasBody = false;
                }
            }


            /**
             * 2 解析方法參數(shù)的注解
             */
            int length = parameterAnnotations.length;
            parameterHandler = new ParameterHandler[length];
            for (int i = 0; i < length; i++) {
                // 一個(gè)參數(shù)上的所有的注解
                Annotation[] annotations = parameterAnnotations[i];
                // 處理參數(shù)上的每一個(gè)注解
                for (Annotation annotation : annotations) {
                    //todo 可以加一個(gè)判斷:如果httpMethod是get請(qǐng)求,現(xiàn)在又解析到Filed注解,可以提示使用者使用Query注解
                    if (annotation instanceof Field) {
                        //得到注解上的value: 請(qǐng)求參數(shù)的key
                        String value = ((Field) annotation).value();
                        parameterHandler[i] = new ParameterHandler.FiledParameterHandler(value);
                    } else if (annotation instanceof Query) {
                        String value = ((Query) annotation).value();
                        parameterHandler[i] = new ParameterHandler.QueryParameterHandler(value);

                    }
                }
            }

            return new ServiceMethod(this);
        }
    }
}

ParameterHandler.java

public abstract class ParameterHandler {

    abstract void apply(ServiceMethod serviceMethod, String value);


    static class QueryParameterHandler extends ParameterHandler {
        String key;

        public QueryParameterHandler(String key) {
            this.key = key;
        }

        //serviceMethod: 回調(diào)
        @Override
        void apply(ServiceMethod serviceMethod, String value) {
            serviceMethod.addQueryParameter(key,value);
        }
    }

    static class FiledParameterHandler extends ParameterHandler {
        String key;

        public FiledParameterHandler(String key) {
            this.key = key;
        }

        @Override
        void apply(ServiceMethod serviceMethod, String value) {
            serviceMethod.addFiledParameter(key,value);
        }
    }
}

定義注解:
Field.java

@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Field {

    String value();
}

GET.java

@Target(METHOD)
@Retention(RUNTIME)
public @interface GET {

    String value() default "";
}

POST.java

@Target(METHOD)
@Retention(RUNTIME)
public @interface POST {

    String value() default "";
}

Query.java

@Target(PARAMETER)
@Retention(RUNTIME)
public @interface Query {

    String value();
}

最后,我們來(lái)看如何使用自定義的Retrofit。

public class MainActivity extends AppCompatActivity {

    private WeatherApi weatherApi;
    private static final String TAG = "MainActivity";
    private EnjoyWeatherApi enjoyWeatherApi;

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

        EnjoyRetrofit enjoyRetrofit = new EnjoyRetrofit.Builder().baseUrl("https://restapi.amap.com").build();
        enjoyWeatherApi = enjoyRetrofit.create(EnjoyWeatherApi.class);
    }

    public void enjoyGet(View view) {
        okhttp3.Call call = enjoyWeatherApi.getWeather("110101", "ae6c53e2186f33bbf240a12d80672d1b");
        call.enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(okhttp3.Call call, IOException e) {

            }

            @Override
            public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
                Log.i(TAG, "onResponse enjoy get: " + response.body().string());
                response.close();
            }
        });

    }

    public void enjoyPost(View view) {
        okhttp3.Call call = enjoyWeatherApi.postWeather("110101", "ae6c53e2186f33bbf240a12d80672d1b");
        call.enqueue(new okhttp3.Callback() {
            @Override
            public void onFailure(okhttp3.Call call, IOException e) {

            }

            @Override
            public void onResponse(okhttp3.Call call, okhttp3.Response response) throws IOException {
                Log.i(TAG, "onResponse enjoy post: " + response.body().string());
                response.close();
            }
        });
    }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容