Retrofit2的GsonConverterFactory.create()和RxJava2CallAdapterFactory.create()的实现过程以及执行过程

Stella981
• 阅读 595

一概述

  上一节分析了retrofit2从创建到执行的完整流程,本节分析一下两个非常重要的功能。数据转换器的实现以及网络请求适配器的实现。

二、GsonConvertFactory.create()数据转换器的实现过程以及执行过程

  我们先看下GsonConvertFactory.crete()的源代码,此类在retrofit-converters插件中

public final class GsonConverterFactory extends Converter.Factory {
  //创建GsonConverterFactory对象
  public static GsonConverterFactory create() {
    return create(new Gson());
  }

  @SuppressWarnings("ConstantConditions") // Guarding public API nullability.
  public static GsonConverterFactory create(Gson gson) {
    if (gson == null) throw new NullPointerException("gson == null");
    return new GsonConverterFactory(gson);
  }

  private final Gson gson;

  private GsonConverterFactory(Gson gson) {
    this.gson = gson;
  }
  //将请求结果的body进行转换
  @Override
  public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations,
      Retrofit retrofit) {
    //这里的TypeAdapter是Gson中用来做序列化和反序列化用的
    //其中TypeToken.get(type)是用来获取一个类类型
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));  //最后返回一个jsonResponse对象做解析工作
    return new GsonResponseBodyConverter<>(gson, adapter);
  }
  //转换请求结果的body
  @Override
  public Converter<?, RequestBody> requestBodyConverter(Type type,
      Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
    TypeAdapter<?> adapter = gson.getAdapter(TypeToken.get(type));
    return new GsonRequestBodyConverter<>(gson, adapter);
  }
}

  我们看一下GsonResponseBodyConverter的源代码,看看其都干了什么事情。

final class GsonResponseBodyConverter<T> implements Converter<ResponseBody, T> {
  private final Gson gson;
  private final TypeAdapter<T> adapter;

  GsonResponseBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }
  //这里的convert方法是Converter接口中定义的convert
  @Override public T convert(ResponseBody value) throws IOException {
    //创建一个JsonReader对象,jsonReader是gson中的类供TypeAdapter序列化以及反序列化时使用的。
    JsonReader jsonReader = gson.newJsonReader(value.charStream());
    try {
      //通过TypeAdapter把读取到的对象转换为泛型
      T result = adapter.read(jsonReader);
      if (jsonReader.peek() != JsonToken.END_DOCUMENT) {
        throw new JsonIOException("JSON document was not fully consumed.");
      }
      //并把泛型结果返回
      return result;
    } finally {
      value.close();
    }
  }
}

看下GsonRequestBodyConverter类

final class GsonRequestBodyConverter<T> implements Converter<T, RequestBody> {
  private static final MediaType MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8");
  private static final Charset UTF_8 = Charset.forName("UTF-8");

  private final Gson gson;
  private final TypeAdapter<T> adapter;

  GsonRequestBodyConverter(Gson gson, TypeAdapter<T> adapter) {
    this.gson = gson;
    this.adapter = adapter;
  }
  //实现Converter接口的convert方法
  @Override public RequestBody convert(T value) throws IOException {
    Buffer buffer = new Buffer();
    Writer writer = new OutputStreamWriter(buffer.outputStream(), UTF_8);
    //创建一个jsonwriter
    JsonWriter jsonWriter = gson.newJsonWriter(writer);
    //把http请求信息进行序列化
    adapter.write(jsonWriter, value);
    jsonWriter.close();
    //然后把序列化后的结果放到RequestBody中
    return RequestBody.create(MEDIA_TYPE, buffer.readByteString());
  }

看下Converter.Factroy的源代码,Converter是一个接口,Factory是其内部抽象类。其中定义了各种类型转换的接口。

//数据转换接口
public interface Converter<F, T> {  //数据转换接口
  @Nullable T convert(F value) throws IOException;

  /** Creates {@link Converter} instances based on a type and target usage. */
  abstract class Factory {
    //转换网络请求的返回结果
    public @Nullable Converter<ResponseBody, ?> responseBodyConverter(Type type,
        Annotation[] annotations, Retrofit retrofit) {
      return null;
    }

    //转换请求结果
    public @Nullable Converter<?, RequestBody> requestBodyConverter(Type type,
        Annotation[] parameterAnnotations, Annotation[] methodAnnotations, Retrofit retrofit) {
      return null;
    }

 具体的转化代码也就这么多,都是操作Gson的一系列方法实现的。下面看看GsonConvertFactory是如何在框架中调用的。

 首先要明确一点,既然是响结果转换器,其必定是响应结果前实现回调的,即在Callback回调函数前需要把响应结果给转换后再给Callback。

 上一节我们说过我们生成的Call其实就是DefaultCallAdapterFactory中的ExecutorCallbackCall。然而ExecutorCallbackCall中却没有看得到Converter的身影。ExecutorCallbackCall的源码如下:

static final class ExecutorCallbackCall<T> implements Call<T> {
    final Executor callbackExecutor;
    final Call<T> delegate;

    ExecutorCallbackCall(Executor callbackExecutor, Call<T> delegate) {
      this.callbackExecutor = callbackExecutor;
      this.delegate = delegate;
    }

    @Override public void enqueue(final Callback<T> callback) {
      Objects.requireNonNull(callback, "callback == null");

      delegate.enqueue(new Callback<T>() {
        @Override public void onResponse(Call<T> call, final Response<T> response) {
          callbackExecutor.execute(() -> {
            if (delegate.isCanceled()) {
              // Emulate OkHttp's behavior of throwing/delivering an IOException on cancellation.
              callback.onFailure(ExecutorCallbackCall.this, new IOException("Canceled"));
            } else {
              callback.onResponse(ExecutorCallbackCall.this, response);
            }
          });
        }

        @Override public void onFailure(Call<T> call, final Throwable t) {
          callbackExecutor.execute(() -> callback.onFailure(ExecutorCallbackCall.this, t));
        }
      });
    }

  那么接着往上一级目录找找,我们记得在HttpMethodService类的内部有一个invoke方法,其会调用他的实现类CallAdapted类的adapt方法,方法内部会调用DefaultCallAdapterFactory的adapt方法并返回一个ExecutorCallbackCall。并传入一个Call和方法的参数。这个Call的实现类就是HttpCall。你没看错响应结果转换就发生在这个类中。

@Override final @Nullable ReturnT invoke(Object[] args) {
    Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);
    return adapt(call, args);
  }

  protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);

  static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
    private final CallAdapter<ResponseT, ReturnT> callAdapter;

    CallAdapted(RequestFactory requestFactory, okhttp3.Call.Factory callFactory,
        Converter<ResponseBody, ResponseT> responseConverter,
        CallAdapter<ResponseT, ReturnT> callAdapter) {
      super(requestFactory, callFactory, responseConverter);
      this.callAdapter = callAdapter;
    }

    @Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
      return callAdapter.adapt(call);
    }
  }

 既然已经确定了响应数据转换在HttpCall中发生。那我们看下他具体做了什么事情。

final class OkHttpCall<T> implements Call<T> {
  private final RequestFactory requestFactory;
  private final Object[] args;
  private final okhttp3.Call.Factory callFactory;
  private final Converter<ResponseBody, T> responseConverter;

  private volatile boolean canceled;

  @GuardedBy("this")
  private @Nullable okhttp3.Call rawCall;
  @GuardedBy("this") // Either a RuntimeException, non-fatal Error, or IOException.
  private @Nullable Throwable creationFailure;
  @GuardedBy("this")
  private boolean executed;

  OkHttpCall(RequestFactory requestFactory, Object[] args,
      okhttp3.Call.Factory callFactory, Converter<ResponseBody, T> responseConverter) {
    this.requestFactory = requestFactory;
    this.args = args;
    this.callFactory = callFactory;
    this.responseConverter = responseConverter;
  }

  果然如上面我们分析的那样,HttpCall接收了一个Converter<ResponseBody,T> responseConverter变量。而这个变量就是我们创建retrofit设置的GsonConvertFactory.create()。

  在HttpCall的内部有一个enqueue(callback)方法,其内部会调用parseResponse方法。parseResponse内部会调用convert方法对数据进行转换。这个convert方法就是上面我们提到的GsonResponseBodyConvert。

  HttpCall的enqueue方法

@Override public void enqueue(final Callback<T> callback) {
    ...省略了前面的一些代码
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          throwIfFatal(e);
          callFailure(e);
          return;
        }

        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          throwIfFatal(t);
          t.printStackTrace(); // TODO this is not great
        }
      }

    ....省略了一些代码
    });
  }

  接下来看下parseResponse方法

Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {
    ResponseBody rawBody = rawResponse.body();

    // Remove the body's source (the only stateful object) so we can pass the response along.
    rawResponse = rawResponse.newBuilder()
        .body(new NoContentResponseBody(rawBody.contentType(), rawBody.contentLength()))
        .build();...省略了一些diamante

    ExceptionCatchingResponseBody catchingBody = new ExceptionCatchingResponseBody(rawBody);
    try {
      //在此处做转换操作,这里的responseConverter就是GsonResponseBodyConverter
      T body = responseConverter.convert(catchingBody);
      //把body设置进去
      return Response.success(body, rawResponse);
    } catch (RuntimeException e) {
      catchingBody.throwIfCaught();
      throw e;
    }
  }

  到这里转换操作就完成了,converter方法的具体实现可以参考上面的GsonResponseBodyConverter源码。

总结:通过Retrofit设置响应数据转换器GsonConvertFactory.create(),在HttpCall中的enqueue方法中的parseResponse做具体的转换,转换调用的是responseConverter.convert(catchingBody)。其中responseConverter的具体转换操作是在GsonResponseBodyConverter中完成的。好了GsonConvertFactory.create()的源码以及执行流程就这样分析完了。

  ps:补充一下自定义响应数据解析器

  根据上面的分析我们知道创建响应数据解析器的时候解析器需要继承Converter.Factory,并实现Converter.Factory的responseBodyConverter接口。那么我们在自定义响应数据解析器的时候也可以这么干。三部搞定

  1.创建一个XmlConverterFactory并继承Converter.Factory的并实现其responseBodyConverter方法

  2.创建一个具体的解析类,并当做responseBodyConverter的返回值

  3.在retrofit的Builder中注解解析器XmlConverterFactory.create()

  下面是以上三步的示例代码:

/**
 * xml响应数据解析器
 * create by yangwei
 * on 2020-02-25 20:12
 */
public class XmlConverterFactory extends Converter.Factory {
    public static XmlConverterFactory create() {
        return new XmlConverterFactory(new Xml());
    }

    public static XmlConverterFactory create(Xml xml) {
        return new XmlConverterFactory(xml);
    }

    @javax.annotation.Nullable
    @Override
    public Converter<ResponseBody, ?> responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {

        return new XmlResponseBodyFactory();
    }
}

/**
 * create by yangwei
 * on 2020-02-25 20:16
 */
public class XmlResponseBodyFactory implements Converter<T, RequestBody> {
    private static final MediaType MEDIA_TYPE = MediaType.get("application/json; charset=UTF-8");
    @javax.annotation.Nullable
    @Override
    public RequestBody convert(T value) throws IOException {
        //此处做具体转换操作
        return RequestBody.create(MEDIA_TYPE,value.bytes);
    }
}

  在Retrofit的Builder中设置其网络请求数据转换器

Retrofit retrofit = new Retrofit.Builder().
                baseUrl("http://www.xxxx.com/").//请求地址
                addConverterFactory(XmlConverterFactory.create()).//自定义xml网络请求结果转换器
                addCallAdapterFactory(RxJava2CallAdapterFactory.create()).//网络请求适配器
                build();

  自定义的示例代码就写完了,大家在实际的开发过程中可以根据需要自行添加自定义网络请求结果转换器,或者对转换器进行修正。

三、RxJava2CallAdapterFactory.create()网络请求适配器的实现过程以及执行过程

  上一节分下了默认的请求网络适配器DefaultCallAdapterFactory。让我们先来回顾下它具体是如何工作的。

  首先在retrofit的builder中进行设置,不设置也行默认就是使用的DefaultCallAdapterFactory请求网络适配器。

  在上一节中我们通过retrofit的create方法动态生成了一个接口实例,并调用接口实例的方法返回了一个Call,生成过程是调用loadServiceMethod.invoke(map)方法,ServiceMethod是个抽象类, 其实现类是HttpServiceMethod,在HttpServiceMethod中重新会真正的调用invoke方法,并实例化一个OkHttpCall,通过其HttpServiceMethod的invoke方法,在其方法内部会调用DefaultCallAdapterFactory.adapt方法,DefaultCallAdapterFactory的adapt方法会生成一个ExecutorCallbackCall并返回。

  下面说说RxJava2CallAdapterFactory网络请求适配器是如何实现的,其实原理和前面说的也是差不多的,至少前半部分是相同的。

  先用文本描述一下流程,然后后面说源码的时候就比较清晰了:

  1.在创建Retrofit的时候添加网络请求适配器,即添加RxJava2CallAdapterFactory的能力。

  2.然后在retrofit的中有一个callAdapter方法,其内部会调用nextCallAdapter方法,通过网络适配器工厂集合callAdapterFactories.get(i).get(returntype,annotations,this)方法返回一个适配器,.get(i)方法返回的这个适配器就是我们设置的RxJava2CallAdapterFactory,后面的get(retrunType,annotations,this)方法返回的则是集成CallAdapter接口的RxJava2CallAdapter类。

  3.接着会调用Retrofit的create中的代理方法loadServiceMethod的invoke方法。invoke方法最终是在HttpServiceMethod方法中执行的,并且invoke方法会调用RxJava2CallAdapterFactory中的adapt方法,并传入一个OkHttpCall。在adapt的内部会根据同步调用或者异步调用返回一个ResultObservable的被观察者,ResultObservable里面封装了CallEnqueueObservable或者CallExecuteObservable,而以上两个Observable又封装了OkHttpCall,所以ResultObservable间接持有了OkHttpCall,到此我们需要的Observable也就生成了,其生成步骤可以简化为:retrofit.create->loadServiceMethod.invoke->ServiceMethod.invoke->HttpServiceMethod.invoke->RxJava2CallAdapterFactory.adapt->ResultObservable。

  4.执行过程,当ResultObservable调用其subscribe方法的时候最终会调用其subscribeActual方法,在其subscribeActual内部会调用CallEnqueueObservable的subscribe方法并把ResultObserver当做参数传递过去。在CallEnqueueObservable的subscribe方法中又会调用其自身的subscribeActual方法实现观察者与被观察者的最终绑定,并且会创建一个CallCallback对象,并调用OkHttpCall的enqueue方法执行网络请求,参数就是刚刚创建的CallBbackCall,其持有observer的引用和OkHttpCall的引用。在OkHttpCall的enqueu方法内部会调用网络请求真正的Call(okhttp3.Call)的enqueue方法发起真正的网络请求,并将网络请求的执行结果写入刚刚传进来的CallBackCall的onResponse方法。而CallBackCall的onResponse方法又会回调ResultObservable的onNext方法,而ResultObservable的onNext方法又会调用LambdaObserver的onNext方法,LambdaObserver.onNext方法会最终调用观察这个的回调接口,从而完成整个调用过程。

a.添加适配器

addCallAdapterFactory(RxJava2CallAdapterFactory.create()).//网络请求适配器

  b.retrofit中返回CallAdaper网络适配器

public CallAdapter<?, ?> nextCallAdapter(@Nullable CallAdapter.Factory skipPast, Type returnType,
      Annotation[] annotations) {
    Objects.requireNonNull(returnType, "returnType == null");
    Objects.requireNonNull(annotations, "annotations == null");

    int start = callAdapterFactories.indexOf(skipPast) + 1;
    for (int i = start, count = callAdapterFactories.size(); i < count; i++) {
      //这是返回网络适配器的核心代码
      CallAdapter<?, ?> adapter = callAdapterFactories.get(i).get(returnType, annotations, this);
      if (adapter != null) {
        return adapter;
      }
    }

  c.创建ResultObservable

  HttpServiceMethod中调用invoke方法,并执行RxJava2CallAdapter.adapt(OkHttpCall)

static final class CallAdapted<ResponseT, ReturnT> extends HttpServiceMethod<ResponseT, ReturnT> {
    private final CallAdapter<ResponseT, ReturnT> callAdapter;

    CallAdapted(RequestFactory requestFactory, okhttp3.Call.Factory callFactory,
        Converter<ResponseBody, ResponseT> responseConverter,
        CallAdapter<ResponseT, ReturnT> callAdapter) {
      super(requestFactory, callFactory, responseConverter);
      this.callAdapter = callAdapter;
    }

    @Override protected ReturnT adapt(Call<ResponseT> call, Object[] args) {
      //返回ResultObservable
      return callAdapter.adapt(call);
    }
  }

  以上callAdapter的真正实现是RxJava2CallAdapter,所以我们直接看它的代码

@Override public Object adapt(Call<R> call) {
    //根据同步或者异步创建Observable
    Observable<Response<R>> responseObservable = isAsync
        ? new CallEnqueueObservable<>(call)
        : new CallExecuteObservable<>(call);

        //把创建好的Observable包装秤ResultObservable
    Observable<?> observable;
    if (isResult) {
      observable = new ResultObservable<>(responseObservable);
    } else if (isBody) {
      observable = new BodyObservable<>(responseObservable);
    } else {
      observable = responseObservable;
    }

  到此处ResultObservable已经创建完成了,下面看看其是怎样执行的

d.执行过程

  执行过程的开始会调动ResultObservable的subscribe对观察者和被观察者进行绑定,其最终会只执行ResultObservable的subscribeActual方法

@Override protected void subscribeActual(Observer<? super Result<T>> observer) {
    upstream.subscribe(new ResultObserver<T>(observer));
  }

  从这段代码中我们可以看穿其仅仅执行了订阅而已,其中upstream指的是CallEnqueuObservable或者CallExecuteObservable,这里的参数中的Observer指的是LambdaObserver。

  我们看下ResultObserver都干了些啥,主要看onNext方法

private static class ResultObserver<R> implements Observer<Response<R>> {
    private final Observer<? super Result<R>> observer;

    ResultObserver(Observer<? super Result<R>> observer) {
      this.observer = observer;
    }

    @Override public void onSubscribe(Disposable disposable) {
      observer.onSubscribe(disposable);
    }

    @Override public void onNext(Response<R> response) {
      observer.onNext(Result.response(response));
    }

  在onNext方法中仅仅是调用了LambdaObserver的onNext方法而已。

回过头来看EnQueueObservable的subscribe,由于subscribe最终会调用subscribeActual方法,所以我们直接看其这个方法就行了

@Override protected void subscribeActual(Observer<? super Response<T>> observer) {
    //这里的Call指的是OkHttpCall
    Call<T> call = originalCall.clone();
    //创建一个CallCallback会回调类,并将网络请求call和ResultObserver传递进去
    CallCallback<T> callback = new CallCallback<>(call, observer);
    //绑定
    observer.onSubscribe(callback);
    if (!callback.isDisposed()) {
      //执行OkHttpCall的enqueue方法并把CallCallbac实例传递进去
      call.enqueue(callback);
    }
  }

  我们接着看OkHttpCall的enqueue都干了些啥

@Override public void enqueue(final Callback<T> callback) {
    Objects.requireNonNull(callback, "callback == null");
    okhttp3.Call call;
    ...省略了一些代码
    call.enqueue(new okhttp3.Callback() {
      @Override public void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {
        Response<T> response;
        try {
          response = parseResponse(rawResponse);
        } catch (Throwable e) {
          throwIfFatal(e);
          callFailure(e);
          return;
        }

        try {
          callback.onResponse(OkHttpCall.this, response);
        } catch (Throwable t) {
          throwIfFatal(t);
          t.printStackTrace(); // TODO this is not great
        }
      }
  ...省略了下面的代码
  }

  这个方法非常的明确,其利用okhttp3的Call的enqueu发起真正的网络请求并把回调结果写入CallCallback中的onResponse中。

  在CallCallback的onResponse方法中会调用ResultObserver的onNext方法,并把响应就结果传入

@Override public void onResponse(Call<T> call, Response<T> response) {
      if (disposed) return;

      try {
        observer.onNext(response);

  紧接着ResultObserver的onNext方法又会调用LambdaObserver的onNext方法并传入响应结果,再接着LambdaObserver的onNext方法又会调用观察者设置的回调函数。到此为止整个执行过程就已经结束了。

ps:自定义网络请求适配器的方法和数据转换器的步骤类似,这里简单说一下

  1.首选定义一个继承自CallAdapter.Factory的类并实现其方法,在其方法中返回真正适配的类,即实现了CallAdapter接口的类。

  2.定义一个实现了CallAdapter接口的类,在这个中完成真正的适配

  3.在创建retrofit的时候在其Builder中设置这个适配器即可。

四、总结

经过分析GsonConverterFactory和RxJava2CallAdapterFactory相信大家对retrofit的理解又有一些更深的感悟了,实时求实的说多读读开源代码对技能和内功的修炼是非常有帮助的。

点赞
收藏
评论区
推荐文章
亚瑟 亚瑟
3年前
Flutter - 深入理解Flutter应用启动
基于Flutter1.5,从源码视角来深入剖析flutter应用的启动流程,相关源码目录见文末附录一、概述上一篇文章已经介绍了FlutterApplication和FlutterActivity的onCreate()方法执行过程,并触发Flutter引擎的启动,并最终执行到runApp(Widgetapp)方法,这才刚刚开始执行dart的
全栈角度看分页处理
分页是webapplication开发最常见的功能。在使用不同的框架和工具过程中,发现初始行/页的定义不同,特意整理记录。从这个技术点去看不同层的实现。以及不同语言实现的对比。文章会从正常的web结构分层的角度去梳理不同层的处理。分为数据库分页、服务端分页、前端分页
源码学习之MyBatis的底层查询原理
导读本文通过MyBatis一个低版本的bug(3.4.5之前的版本)入手,分析MyBatis的一次完整的查询流程,从配置文件的解析到一个查询的完整执行过程详细解读MyBatis的一次查询流程,通过本文可以详细了解MyBatis的一次查询过程。在
Wesley13 Wesley13
3年前
mysql 执行流程解析
MySQL可以分为Server层和存储引擎层两部分Server层包括连接器、查询缓存、分析器、优化器、执行器等,涵盖MySQL的大多数核心服务功能,以及所有的内置函数,所有跨存储引擎的功能都在这一层实现,比如存储过程、触发器、视图等而存储引擎层负责数据的存储和提取。其架构模式是插件式的,支持InnoDB、MyISAM、Memory
邢德全 邢德全
9个月前
数据采集在制造业中的应用场景
万界星空科技生产制造执行MES系统具有能够帮助企业实现生产数据收集与分析、生产计划管理、生产过程监控等的功能板块,数据采集是MES系统进行数据统计与生产管理等后续工作的基础,MES设备数据采集是实现智能制造的重要环节,能够实现设备智能化、数据化决策、提高生产效率和降低成本等优点。
Python进阶者 Python进阶者
9个月前
Excel中这日期老是出来00:00:00,怎么用Pandas把这个去除
大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据筛选的问题。问题如下:这日期老是出来00:00:00,怎么把这个去除。二、实现过程后来【论草莓如何成为冻干莓】给了一个思路和代码如下:pd.toexcel之前把这
稳,从数据库连接池 testOnBorrow 看架构设计 | 京东云技术团队
本文从CommonsDBCPtestOnBorrow的作用机制着手,管中窥豹,从一点去分析数据库连接池获取的过程以及架构分层设计。以下内容会按照每层的作用,贯穿分析整个调用流程。
并发编程-FutureTask解析 | 京东物流技术团队
通过本文可以了解FutureTask任务执行的方式以及Future.get已阻塞的方式获取线程执行的结果原理,并且从代码中可以了解FutureTask的任务执行状态以及状态的变化过程。
小万哥 小万哥
11个月前
SQL(Structured Query Language)简介和常见 SQL 命令示例
简介SQL(StructuredQueryLanguage)是一种用于访问和操作关系型数据库的标准语言。它是一个功能强大的语言,用于执行各种数据库操作,包括检索数据、插入新记录、更新记录、删除记录、创建数据库、创建新表、设置权限以及执行存储过程和视图等。以