当前位置: 首页 > news >正文

谈谈我对Retrofit源码的理解

文章目录

  • 一、Retrofit简介
  • 二、使用介绍
    • 2.1 app / build.gradle添加依赖
    • 2.2 创建 Retrofit 实例
    • 2.3 创建 API 接口定义文件
    • 2.4 使用 Retrofit 进行网络请求
  • 三、源码分析
    • 3.1 创建 Retrofit 实例: 建造者模式创建Retrofit
    • 3.2 实例化API接口: 动态代理模式
    • 3.3 获取Observable返回值
      • 3.3.1 ServiceMethod.java
      • 3.3.2 HttpServiceMethod.java
      • 3.3.3 HttpServiceMethod.java
      • 3.3.4 CallAdapted.java
      • 3.3.5 RxJava3CallAdapter.java
    • 3.4 发起网络请求
      • 3.4.1 CallEnqueueObservable.java
      • 3.4.2 OkHttpCall.java
      • 3.4.3 GsonResponseBodyConverter.java
  • 四、参考文档

一、Retrofit简介

square/retrofit Github地址 最新版本从这里获取!

Retrofit 是一款由 Square 公司开发的用于 Android 和 Java 应用程序的网络请求库,旨在简化 HTTP 网络请求的过程,Retrofit 最初是在 2013 年开源发布的,已经风靡了10年。其优点如下

  • 简化网络请求的过程:Retrofit 可以根据 API 接口定义文件自动生成网络请求代码,从而避免了手动创建网络请求代码的繁琐工作。
  • 方便处理网络请求:Retrofit 支持使用注解(如 @GET、@POST 等)来指定网络请求的方法和参数,同时支持异步网络请求和回调处理,方便处理网络请求和响应。
  • 易于定制和扩展:Retrofit 提供了一些定制和扩展机制,如自定义网络请求和响应处理器、支持多种转换器(如 JSON 转换器、XML 转换器等)等,可以满足不同的网络请求需求
  • 支持缓存:Retrofit 支持网络请求结果的缓存,可以在没有网络连接时提供离线数据访问的功能
  • 基于 OkHttp:Retrofit 是基于 OkHttp 实现的,OkHttp 是一个高效、灵活和易于使用的网络请求库,因此 Retrofit 也具有 OkHttp 的优点,如连接池、请求队列等

二、使用介绍

2.1 app / build.gradle添加依赖

dependencies {implementation 'com.squareup.retrofit2:retrofit:2.9.0'implementation 'com.squareup.retrofit2:converter-gson:2.9.0'// 下面三个是rxjava相关的依赖,如果你的项目不使用rxjava可以不用依赖//  注意adapter-rxjava3,如果你的rxjava版本是2,那这里是adapter-rxjava2,这个自己网上找吧implementation 'com.squareup.retrofit2:adapter-rxjava3:2.9.0'implementation 'io.reactivex.rxjava3:rxandroid:3.0.2'implementation 'io.reactivex.rxjava3:rxjava:3.1.5'
}

2.2 创建 Retrofit 实例

Retrofit retrofit = new Retrofit.Builder().baseUrl("https://api.example.com/").addCallAdapterFactory(RxJava3CallAdapterFactory.create()) // rxjava call.addConverterFactory(GsonConverterFactory.create()) // json解析.build()             

2.3 创建 API 接口定义文件

具体如何定义可以参考Retrofit 官方文档介绍学习相关注解的使用方法

public interface IService {@GET("app/update")Observable<Integer> getUpdateInfo();@GET("app/test")Call<Integer> getTestInfo();
}

2.4 使用 Retrofit 进行网络请求

IService apiService = retrofit.create(IService.class);apiService.getUpdateInfo().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Integer>() {@Overridepublic void onSubscribe(@NonNull Disposable d) {}@Overridepublic void onNext(@NonNull Integer integer) {}@Overridepublic void onError(@NonNull Throwable e) {}@Overridepublic void onComplete() {}});}

三、源码分析

通过上面四步我们可以使用Retrofit发起一个完整的网络请求, 接下来让我们学习一下源码

3.1 创建 Retrofit 实例: 建造者模式创建Retrofit

Retrofit.java
Retrofit retrofit = new Retrofit.Builder().baseUrl(BASE_URL).addCallAdapterFactory(RxJava3CallAdapterFactory.create()).addConverterFactory(GsonConverterFactory.create()).build();
Retrofit.java 的 Retrofit.Builderpublic Retrofit build() {if (baseUrl == null) {throw new IllegalStateException("Base URL required.");}// 对应 Builder.client() 如果你赋值的话,这里就不会为nullokhttp3.Call.Factory callFactory = this.callFactory;if (callFactory == null) {// 没有定义的话默认使用OkHttpClientcallFactory = new OkHttpClient();}// 对应 Builder.callbackExecutor(), 如果你赋值的话,这里就不会为nullExecutor callbackExecutor = this.callbackExecutor;if (callbackExecutor == null) {// platform 是Android, defaultCallbackExecutor()  --> new MainThreadExecutor()// MainThreadExecutor 创建一个主线程的Looper Handler,当异步任务处理完毕会把结果通过post的方式发到主线程callbackExecutor = platform.defaultCallbackExecutor();}// 对应 Builder.addCallAdapterFactory(), 我们使用了RxJava3CallAdapterFactory.create() -->//  RxJava3CallAdapterFactoryList<CallAdapter.Factory> callAdapterFactories = new ArrayList<>(this.callAdapterFactories);// java8 开始新增 CompletableFutureCallAdapterFactory + DefaultCallAdapterFactory// java8 以下只有 DefaultCallAdapterFactorycallAdapterFactories.addAll(platform.defaultCallAdapterFactories(callbackExecutor));List<Converter.Factory> converterFactories =new ArrayList<>(1 + this.converterFactories.size() + platform.defaultConverterFactoriesSize());converterFactories.add(new BuiltInConverters());// 对应 Builder.addConverterFactory(), 我们使用了GsonConverterFactory.create() --> GsonConverterFactoryconverterFactories.addAll(this.converterFactories);converterFactories.addAll(platform.defaultConverterFactories());return new Retrofit(callFactory,baseUrl,unmodifiableList(converterFactories),unmodifiableList(callAdapterFactories),callbackExecutor,validateEagerly);}}

目前Retrofit的相关信息已配置完毕

Retrofit(okhttp3.Call.Factory callFactory,HttpUrl baseUrl,List<Converter.Factory> converterFactories,List<CallAdapter.Factory> callAdapterFactories,@Nullable Executor callbackExecutor,boolean validateEagerly) {this.callFactory = callFactory;this.baseUrl = baseUrl;this.converterFactories = converterFactories; // Copy+unmodifiable at call site.this.callAdapterFactories = callAdapterFactories; // Copy+unmodifiable at call site.this.callbackExecutor = callbackExecutor;this.validateEagerly = validateEagerly;}

3.2 实例化API接口: 动态代理模式

IService apiService = retrofit.create(IService.class);

多年前分析Retrofit源码的时候我也很懵逼, 啥是动态代理? 为啥要用动态代理? 从官方的介绍或者其他博主人云亦云的观点把我搞得一头雾水,其实这个问题很简单!不是因为动态代理才有需求,而是因为需求才有动态代理

假设你是Retrofit的开发人员, 现在使用Retrofit的人都会给你传参数(url,requestMethod,header,params), 你该怎么设计框架才可以拿到用户的参数?

大部分人的第一反应就是: 暴露个方法呗! 那要是参数可变呢?重载! 那url散落代码各处梳理不便呢?em…

聪明的你开始想到了注解!创建一个网络请求接口A,里面每个方法对应每个网络请求,方法的注解对应着需要的参数,再创建一个实现类Aimpl,在Aimpl的每个方法中去反射获取A对应方法的注解然后去发起请求。

我们继续思考一下,Aimpl除了解析不同的方法外,基本上内容完全相同?如果存在接口ABCD,是不是还得对应生成Aimpl Bimpl Cimpl Dimpl这样的实现类?有没有什么办法可以再优化一下? 有! 这就是动态代理

篇幅有限这里就不展开动态代理的官方介绍了,有兴趣的同学可以自行查询下。

 public <T> T create(final Class<T> service) {// 这里会校验这个class是不是接口,不是接口就会直接抛异常validateServiceInterface(service);// Proxy.newProxyInstance 动态代理典型用法, 内部通过newInstance反射创建一个实例return (T)Proxy.newProxyInstance(service.getClassLoader(),new Class<?>[] {service},// 这个InvocationHandler相当于一个Hook,只要你调用了这个实例的方法都会回调invokenew InvocationHandler() {private final Platform platform = Platform.get();private final Object[] emptyArgs = new Object[0];@Overridepublic @Nullable Object invoke(Object proxy, Method method, @Nullable Object[] args)throws Throwable {// 如果是Object类的话就不hook了,执行Object的方法吧if (method.getDeclaringClass() == Object.class) {return method.invoke(this, args);}args = args != null ? args : emptyArgs;// platform 是Android, isDefaultMethod 判断这个接口的方法是不是default方法, Java8开始支持接口内增加有实现体的 default方法,这里的意思是: 我只拦截没有实现的方法,有实现体的方法不会拦截return platform.isDefaultMethod(method)? platform.invokeDefaultMethod(method, service, proxy, args)// loadServiceMethod 是重头戏的开始,等会继续分析: loadServiceMethod(method).invoke(args);}});}

3.3 获取Observable返回值

apiService.getUpdateInfo()  --> 触发上文提到的 loadServiceMethod
ServiceMethod<?> loadServiceMethod(Method method) {// retrofit不会无脑create一次,反射一次,也会有缓存机制的ServiceMethod<?> result = serviceMethodCache.get(method);if (result != null) return result;synchronized (serviceMethodCache) {result = serviceMethodCache.get(method);if (result == null) {result = ServiceMethod.parseAnnotations(this, method);serviceMethodCache.put(method, result);}}return result;}

3.3.1 ServiceMethod.java

static <T> ServiceMethod<T> parseAnnotations(Retrofit retrofit, Method method) {// 对注解的校验&&解析headers,method,baseUrl,contentType 等信息RequestFactory requestFactory = RequestFactory.parseAnnotations(retrofit, method);// ....return HttpServiceMethod.parseAnnotations(retrofit, method, requestFactory);}

3.3.2 HttpServiceMethod.java

这里有对Kotlin suspend函数的处理,以后再讲,我们还是分析Java的流程

static <ResponseT, ReturnT> HttpServiceMethod<ResponseT, ReturnT> parseAnnotations(Retrofit retrofit, Method method, RequestFactory requestFactory) {// ....// 结合3.1 创建Retrofit实例的代码  // Retrofit.callAdapter -->  Retrofit.nextCallAdapter --> callAdapterFactories.get().get()// --> RxJava3CallAdapterCallAdapter<ResponseT, ReturnT> callAdapter =createCallAdapter(retrofit, method, adapterType, annotations);// Retrofit.responseBodyConverter --> Retrofit.nextResponseBodyConverter --> converterFactories.responseBodyConverter --> GsonResponseBodyConverterConverter<ResponseBody, ResponseT> responseConverter =createResponseConverter(retrofit, method, responseType);// callFactory 默认OkHttpClientokhttp3.Call.Factory callFactory = retrofit.callFactory;if (!isKotlinSuspendFunction) {// 回到3.2 loadServiceMethod(method)最终返回的是 CallAdapted, 而CallAdapted没有实现invoke方法而是其父类HttpServiceMethod实现return new CallAdapted<>(requestFactory, callFactory, responseConverter, callAdapter);} else{//.....}}

3.3.3 HttpServiceMethod.java

@Overridefinal @Nullable ReturnT invoke(Object[] args) {// OkHttpCall是Retrofit对OkHttp的包装Call<ResponseT> call = new OkHttpCall<>(requestFactory, args, callFactory, responseConverter);return adapt(call, args);}// adapt是抽象方法,具体实现还是交给了CallAdapted
protected abstract @Nullable ReturnT adapt(Call<ResponseT> call, Object[] args);

3.3.4 CallAdapted.java

@Override
protected ReturnT adapt(Call<ResponseT> call, Object[] args) {// 3.3.2 提到 callAdapter 就是 RxJava3CallAdapterreturn callAdapter.adapt(call);}

3.3.5 RxJava3CallAdapter.java

 @Overridepublic Object adapt(Call<R> call) {// 由于我们使用的 RxJava3CallAdapterFactory.create() 默认 isAsync 传了true,所以这里最终返回的就是 CallEnqueueObservableObservable<Response<R>> responseObservable =isAsync ? new CallEnqueueObservable<>(call) : new CallExecuteObservable<>(call);// ... } else {observable = responseObservable;}// ...return RxJavaPlugins.onAssembly(observable);}
}

所以 apiService.getUpdateInfo() 最终返回的是 CallEnqueueObservable

3.4 发起网络请求

我在Rxjava线程切换原理终于在2023年有了答案这篇博客中提到:Rxjava最重要的一个环节就是subscribe, 正是有了这个环节才会产生 订阅流和回调流

// 直接去看 CallEnqueueObservable 的 subscribeActual , 这里如果跟不上的话可以去看一下我的Rxjava的博客
apiService.getUpdateInfo().subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()).subscribe(new Observer<Integer>() {@Overridepublic void onSubscribe(@NonNull Disposable d) {}@Overridepublic void onNext(@NonNull Integer integer) {}@Overridepublic void onError(@NonNull Throwable e) {}@Overridepublic void onComplete() {}});

3.4.1 CallEnqueueObservable.java

@Overrideprotected void subscribeActual(Observer<? super Response<T>> observer) {// Since Call is a one-shot type, clone it for each new observer.Call<T> call = originalCall.clone();// 根据 3.3.3 call就是OkHttpCallCallCallback<T> callback = new CallCallback<>(call, observer);observer.onSubscribe(callback);if (!callback.isDisposed()) {// 真正发起网络请求的地方开始了call.enqueue(callback);}}

3.4.2 OkHttpCall.java

 @Overridepublic void enqueue(final Callback<T> callback) {// ...call = rawCall = createRawCall();// ....call.enqueue(new okhttp3.Callback() {@Overridepublic void onResponse(okhttp3.Call call, okhttp3.Response rawResponse) {//...response = parseResponse(rawResponse);callback.onResponse(OkHttpCall.this, response);//...}@Overridepublic void onFailure(okhttp3.Call call, IOException e) {callFailure(e);}});}
private okhttp3.Call createRawCall() throws IOException {// 结合代码 3.3.2 callFactory 其实就是 OkHttpClient --> RealCall.newRealCall 这里就进入了OkHttp的代码,以后还会分析OKHttp,这里就到此为止okhttp3.Call call = callFactory.newCall(requestFactory.create(args));if (call == null) {throw new NullPointerException("Call.Factory returned null.");}return call;}
 Response<T> parseResponse(okhttp3.Response rawResponse) throws IOException {// ... 这会处理code码,比如 code < 200 || code >= 300 code == 204 || code == 205等// 根据3.3.2 responseBodyConverter --> GsonResponseBodyConverter 就是这里的 responseConverterT body = responseConverter.convert(catchingBody);// ...}

3.4.3 GsonResponseBodyConverter.java

@Overridepublic T convert(ResponseBody value) throws IOException {// Json解析JsonReader jsonReader = gson.newJsonReader(value.charStream());try {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();}}

拿到请求结果后再根据RxJava的回调流回调给订阅者完成整个请求的全部流程

四、参考文档

Retrofit 源码分析
一定能看懂的 Retrofit 最详细的源码解析!
Android 网络框架之Retrofit源码解析
【面试 反思】Retrofit源码与设计 7 连问

相关文章:

谈谈我对Retrofit源码的理解

文章目录一、Retrofit简介二、使用介绍2.1 app / build.gradle添加依赖2.2 创建 Retrofit 实例2.3 创建 API 接口定义文件2.4 使用 Retrofit 进行网络请求三、源码分析3.1 创建 Retrofit 实例: 建造者模式创建Retrofit3.2 实例化API接口: 动态代理模式3.3 获取Observable返回值…...

八股文(三)

目录 一、 如何理解原型与原型链 二、 js继承 三、 vuex的使用 1.mutation和action的区别 mutation action 2.Vuex都有哪些API 四、 前端性能优化方法 五、 类型判断 题目 &#xff08;1&#xff09;typeof判断哪个类型会出错&#xff08;即结果不准确&#xff09;&…...

2023最新实施工程师面试题

1、两电脑都在同一个网络环境中,A 电脑访问不到 B 电脑的共享文件。此现象可能是哪些 方面所导致?怎样处理? 答:首先你要确定是不是在一个工作组内,只有在一个工作组内才可以共享文件,然后看一个看一看有没有防火墙之类的,然后确定文件是不是已经共享 2、 电脑开机时风扇…...

安卓逆向_6 --- JNI 和 NDK

Java 本机接口规范内容&#xff1a;https://docs.oracle.com/en/java/javase/19/docs/specs/jni/index.html JNI官方中文资料&#xff1a;https://blog.csdn.net/yishifu/article/details/52180448 NDK 官方文档&#xff1a;https://developer.android.google.cn/training/ar…...

Pod控制器

K8S之控制器详解#简介#在kubernetes中&#xff0c;按照Pod的创建方式可以将其分为两类&#xff1a;自主式:kubernetes直接创建出来的Pod&#xff0c;这种Pod删除后就没有了&#xff0c;也不会重建。控制器创建pod:通过Pod控制器创建的Pod&#xff0c;这种Pod删除之后还会自动重…...

微服务到云原生

微服务到云原生 微服务 微服务架构&#xff08;Microservice Architecture&#xff09;是一种架构概念&#xff0c;旨在通过将功能分解到各个离散的服务中以实现对解决方案的解耦。 微服务是一种架构风格&#xff0c;一个大型复杂软件应用由一个或多个微服务组成。系统中的各…...

Spring Security 实现自定义登录和认证(1):使用自定义的用户进行认证

1 SpringSecurity 1.1 导入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-security</artifactId> </dependency>1.2 编写配置类 在spring最新版中禁用了WebSecurityConfigurerAdapter…...

Spring Cloud(微服务)学习篇(七)

Spring Cloud(微服务)学习篇(七) 1.使用代码的方式实现流量限制规则 1.1 变更SentinelController类 1.1.1 加入的代码 //流控限制 (一个或多个资源限流), postConstruct注解的作用是保证项目一启动就会加载,// 一个rule就是一个规则PostConstructpublic void FlowRule(){Li…...

嵌入式安防监控项目——前期知识复习

目录 一、概述 二、C语言 三、数据结构 四、IO进程 五、网络 六、ARM体系结构和接口技术 七、系统移植 八、内核驱动 一、概述 我再报班之前学过51和32&#xff0c;不过都是自学的。报班开始先从应用层入手的&#xff0c;C语言和数据结构。只要是个IT专业的大学这都是必…...

SpringAOP——基础知识

AOP AOP全称是Aspect Oriented Programming 即面向切面编程&#xff0c;是对一类统一事务的集中处理 例如&#xff0c;我们的网页许多地方都需要进行登陆验证&#xff0c;这时就需要在很多地方添加重复的验证代码&#xff0c;而AOP可以集中配置需要登陆验证的地方&#xff0c;…...

kafka3.0安装使用

一&#xff1a;定义 Kafka传 统定义&#xff1a;Kafka是一个分布式的基于发布/订阅模式的消息队列&#xff08;Message Queue&#xff09;&#xff0c;主要应用于大数据实时处理领域。 Kafka最 新定义 &#xff1a; Kafka是 一个开源的 分 布式事件流平台 &#xff08;Event St…...

Centos7(阿里云)_安装Mysql8.0

1.安装MySQL 新人可以试用一个月的阿里云&#xff0c;centos7的 一开始可能确实会自带mariadb&#xff0c;所以可以在网上随便找个教程开始尝试安装MySQL&#xff0c;当然大概率出错&#xff0c;然后此时你的rpm下面已经有了一个版本的mysql安装包。 以我为例&#xff0c;随便…...

【Java】JVM

一、介绍 1.什么是JVM? JVM是一种用于计算设备的规范&#xff0c;它是一个虚构出来的机器&#xff0c;是通过在实际的计算机上仿真模拟各种功能实现的。JVM包含一套字节码指令集&#xff0c;一组寄存器&#xff0c;一个栈&#xff0c;一个垃圾回收堆和一个存储方法域。JVM屏…...

Linux 和数据库笔记-06

今日内容介绍全天内容无需立马掌握MySQL 的高级功能应用数据库设计ER模型定义: E 代表实体(数据表), R 代表联系(数据表之间对应的字段)![image-20200530092701017](Linux 和数据库 day06 随堂笔记.assets/image-20200530092701017.png)关系常见分类一对一一对多多对多外键如果…...

MySQL面试题-事务篇

1.事务的特性&#xff08;ACID&#xff09; 事务&#xff08;Transaction&#xff09;是指一组操作被看作是一个不可分割的工作单元&#xff0c;这组操作要么全部执行成功&#xff0c;要么全部执行失败。事务的特性通常用 ACID 四个单词来描述&#xff0c;它们分别代表原子性&…...

Linux嵌入式开发 | 汇编驱动LED(1)

文章目录&#x1f697; &#x1f697;Linux嵌入式开发 | 汇编驱动LED&#xff08;1&#xff09;&#x1f697; &#x1f697;初始化IO&#x1f697; &#x1f697;STM32&#x1f697; &#x1f697;使能GPIO时钟&#x1f697; &#x1f697;设置IO复用&#x1f697; &#x1f6…...

什么是EventLoop?怎么测试Node或页面的性能

Event Loop 机制大家应该都有了解。本文利用 EventLoop 去做一个有趣的检测node或页面性能的代码&#xff0c;顺便介绍了一下EventLoop&#xff0c;希望对大家有所帮助&#xff01; Event Loop Event Loop 机制大家应该都有了解。我先重复总结一下。 Node.js 和 Javascript 的…...

1018 锤子剪刀布 1025 反转链表

现给出两人的交锋记录&#xff0c;请统计双方的胜、平、负次数&#xff0c;并且给出双方分别出什么手势的胜算最大。 输入格式&#xff1a; 输入第 1 行给出正整数 N&#xff08;≤10 5 &#xff09;&#xff0c;即双方交锋的次数。随后 N 行&#xff0c;每行给出一次交锋的信…...

卷积神经网络的原理及实现

专栏&#xff1a;神经网络复现目录 卷积神经网络 本章介绍的卷积神经网络&#xff08;convolutional neural network&#xff0c;CNN&#xff09;是一类强大的、为处理图像数据而设计的神经网络。 基于卷积神经网络架构的模型在计算机视觉领域中已经占主导地位&#xff0c;当今…...

【C++知识点】重载

✍个人博客&#xff1a;https://blog.csdn.net/Newin2020?spm1011.2415.3001.5343 &#x1f4da;专栏地址&#xff1a;C/C知识点 &#x1f4e3;专栏定位&#xff1a;整理一下 C 相关的知识点&#xff0c;供大家学习参考~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

模型参数、模型存储精度、参数与显存

模型参数量衡量单位 M&#xff1a;百万&#xff08;Million&#xff09; B&#xff1a;十亿&#xff08;Billion&#xff09; 1 B 1000 M 1B 1000M 1B1000M 参数存储精度 模型参数是固定的&#xff0c;但是一个参数所表示多少字节不一定&#xff0c;需要看这个参数以什么…...

Python实现prophet 理论及参数优化

文章目录 Prophet理论及模型参数介绍Python代码完整实现prophet 添加外部数据进行模型优化 之前初步学习prophet的时候&#xff0c;写过一篇简单实现&#xff0c;后期随着对该模型的深入研究&#xff0c;本次记录涉及到prophet 的公式以及参数调优&#xff0c;从公式可以更直观…...

跨链模式:多链互操作架构与性能扩展方案

跨链模式&#xff1a;多链互操作架构与性能扩展方案 ——构建下一代区块链互联网的技术基石 一、跨链架构的核心范式演进 1. 分层协议栈&#xff1a;模块化解耦设计 现代跨链系统采用分层协议栈实现灵活扩展&#xff08;H2Cross架构&#xff09;&#xff1a; 适配层&#xf…...

【2025年】解决Burpsuite抓不到https包的问题

环境&#xff1a;windows11 burpsuite:2025.5 在抓取https网站时&#xff0c;burpsuite抓取不到https数据包&#xff0c;只显示&#xff1a; 解决该问题只需如下三个步骤&#xff1a; 1、浏览器中访问 http://burp 2、下载 CA certificate 证书 3、在设置--隐私与安全--…...

JDK 17 新特性

#JDK 17 新特性 /**************** 文本块 *****************/ python/scala中早就支持&#xff0c;不稀奇 String json “”" { “name”: “Java”, “version”: 17 } “”"; /**************** Switch 语句 -> 表达式 *****************/ 挺好的&#xff…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

使用Matplotlib创建炫酷的3D散点图:数据可视化的新维度

文章目录 基础实现代码代码解析进阶技巧1. 自定义点的大小和颜色2. 添加图例和样式美化3. 真实数据应用示例实用技巧与注意事项完整示例(带样式)应用场景在数据科学和可视化领域,三维图形能为我们提供更丰富的数据洞察。本文将手把手教你如何使用Python的Matplotlib库创建引…...

MySQL 8.0 事务全面讲解

以下是一个结合两次回答的 MySQL 8.0 事务全面讲解&#xff0c;涵盖了事务的核心概念、操作示例、失败回滚、隔离级别、事务性 DDL 和 XA 事务等内容&#xff0c;并修正了查看隔离级别的命令。 MySQL 8.0 事务全面讲解 一、事务的核心概念&#xff08;ACID&#xff09; 事务是…...

Golang——6、指针和结构体

指针和结构体 1、指针1.1、指针地址和指针类型1.2、指针取值1.3、new和make 2、结构体2.1、type关键字的使用2.2、结构体的定义和初始化2.3、结构体方法和接收者2.4、给任意类型添加方法2.5、结构体的匿名字段2.6、嵌套结构体2.7、嵌套匿名结构体2.8、结构体的继承 3、结构体与…...