Java Web 开发技术的演进:从 Servlet、Spring MVC 到 WebFlux 及其竞品分析

news2025/1/20 5:51:53

 

前言

随着互联网技术的快速发展,Web 应用程序在处理海量用户访问和大数据时面临着巨大的挑战。在这个过程中,Java Web 开发技术经历了从 Servlet 到 Spring MVC 再到 WebFlux 的演变。在这篇文章中,我们将探讨这三个技术的发展历程、痛点及解决方案,以及它们如何帮助开发者在不断变化的互联网世界中构建更高效、可扩展和响应式的 Web 应用程序。让我们从 Servlet 的诞生开始,追溯 Java Web 开发技术的发展脉络。

从 Servlet 到 Spring MVC 再到 WebFlux,这个技术的升级迭代过程主要反映了 Web 应用程序的发展和性能需求。下面我将详细介绍每个阶段的技术及其痛点解决方案。

Servlet:

Servlet 是 Java Web 应用程序的基础,它提供了一种用于处理 HTTP 请求的标准 Java 接口。Servlet 是一个面向对象的编程模型,每个 Servlet 类处理特定的请求。在早期的 Web 开发中,Servlet 是一种主流的解决方案。

痛点:

  1. 开发者需要手动处理请求和响应对象,编写大量样板代码。
  2. 不易于管理和组织大型项目的代码结构。

Spring MVC:

为了解决 Servlet 的痛点,Spring MVC 出现了。它是一个基于 Java 的 Web 应用程序框架,可以认为是servlet框架,提供了一套简化 Web 开发的工具和约定。通过使用 Spring MVC,开发者可以更加专注于业务逻辑,而不是底层细节。

改进:

  1. 提供了基于注解的编程模型,降低了开发复杂度。
  2. 提供了强大的依赖注入和控制反转功能,简化了组件之间的解耦。
  3. 支持灵活的 URL 映射和视图解析,方便开发者进行 URL 和视图的管理。

痛点:

Spring MVC是基于阻塞 I/O 的,这意味着在处理高并发请求时,可能会遇到性能瓶颈。
随着响应式编程模型的兴起,传统的 Spring MVC 不再满足现代 Web 应用程序的性能要求。

WebFlux:

为了解决 Spring MVC 中的性能瓶颈,Spring Framework 5.0 推出了 WebFlux。WebFlux 是基于响应式编程模型的,它提供了一种非阻塞、事件驱动的方式来构建 Web 应用程序。

改进:

  1. 支持异步和非阻塞 I/O,能够在高并发场景下更好地利用系统资源。
  2. 提供了两种编程模型:基于注解的编程模型和基于函数的编程模型。
  3. 提供了 WebClient,一个响应式的 HTTP 客户端,可以替代 RestTemplate。
  4. 支持与响应式数据存储(如 MongoDB、Cassandra、Redis 等)进行集成,实现端到端的响应式编程。

为什么说SpringMVC基于阻塞 I/O 就意味着在处理高并发请求时,可能会遇到性能瓶颈?

Spring MVC 基于阻塞 I/O 的原因在于其使用的 Servlet 技术,默认采用一个线程处理一个请求的模型。在这种模型中,当一个请求到达服务器时,服务器会为这个请求分配一个线程来处理。在处理过程中,线程会阻塞等待 I/O 操作(如数据库查询、文件读写等)完成,才能继续执行后续操作。在 I/O 操作期间,线程会处于等待状态,无法处理其他请求。

当面临高并发请求时,阻塞 I/O 模型可能会遇到性能瓶颈,原因如下:

  1. 线程资源有限:服务器的线程资源是有限的。当并发请求量很大时,服务器可能会耗尽可用线程,导致新请求无法得到及时处理。同时,由于每个线程都会消耗一定的内存和 CPU 资源,大量线程可能会导致系统资源紧张,进一步降低性能。

  2. 线程切换开销:当大量请求同时到达服务器时,操作系统需要在不同线程之间进行切换,以保证每个请求都能得到处理。线程切换会消耗 CPU 资源,降低系统的整体效率。

  3. I/O 操作效率低:阻塞 I/O 模型中,线程在等待 I/O 操作完成期间无法处理其他请求。这意味着线程在大部分时间里可能处于等待状态,无法充分利用 CPU 资源。

为了解决这些性能瓶颈,非阻塞 I/O 和响应式编程模型应运而生。在非阻塞 I/O 模型中,线程在发起 I/O 操作后不会等待其完成,而是立即返回,处理其他请求。当 I/O 操作完成时,线程会通过回调或事件的方式继续处理。这种模型充分利用了线程资源,提高了系统的吞吐量和响应能力。WebFlux 就是基于这种非阻塞 I/O 和响应式编程模型的 Web 框架。

WebFlux 实现非阻塞和异步的原理是什么?

WebFlux 是一个基于响应式编程模型的框架,它是 Spring Framework 5.0 中引入的一个新特性。WebFlux 提供了一种用于构建响应式 Web 应用程序的非阻塞、事件驱动的方式。它支持异步的处理流程,能够更好地利用系统资源,从而在高并发场景下提高应用程序的性能和响应能力。

WebFlux 实现非阻塞和异步的原理主要依赖于以下几个关键技术和概念:

  1. 响应式编程模型:响应式编程模型允许开发者以声明式的方式处理数据流和事件驱动的操作。在响应式编程中,开发者可以组合、转换和订阅数据流,而无需关心底层的线程和并发问题。WebFlux 使用 Reactor 库实现响应式编程,Reactor 是一个基于 Java 的响应式编程库,实现了 Reactive Streams 规范。

  2. Reactive Streams:Reactive Streams 是一套处理异步数据流的标准接口,包括 Publisher(发布者)、Subscriber(订阅者)、Subscription(订阅)和 Processor(处理器)。WebFlux 使用 Reactive Streams 提供了一套非阻塞的流式数据处理机制,可以在高并发场景下提高系统的吞吐量和响应能力。

  3. 非阻塞 I/O:WebFlux 使用 Netty 作为底层的网络通信框架,Netty 支持非阻塞 I/O 操作。在非阻塞 I/O 模型中,当发起 I/O 操作时,线程不会阻塞等待操作完成,而是立即返回并继续处理其他任务。当 I/O 操作完成时,线程会通过事件或回调的方式得到通知,从而继续处理后续操作。这种模式充分利用了线程资源,提高了系统的吞吐量和响应能力。

  4. 异步编程:WebFlux 的异步编程主要体现在其对异步 I/O 操作和事件驱动的支持。通过使用响应式编程模型和非阻塞 I/O,WebFlux 能够实现高效的异步请求处理。在这种模型中,线程可以在等待 I/O 操作期间处理其他请求,从而充分利用系统资源。

综上所述,WebFlux 实现非阻塞和异步的原理主要依赖于响应式编程模型、Reactive Streams 规范、非阻塞 I/O 技术以及异步编程。这些技术和概念相互协作,使得 WebFlux 能够在高并发场景下提供高性能和响应能力。

一个使用webflux开发的Demo

以下是一个使用 Spring WebFlux 开发的简单示例,演示了创建一个基本的 RESTful API,用于管理一个简单的书籍清单。

准备工作:

在 Spring Boot 项目中集成 Spring WebFlux 非常简单。以下是集成 WebFlux 的步骤:

  1. 添加依赖:
    首先,您需要在项目的 pom.xml(如果您使用的是 Maven)或 build.gradle(如果您使用的是 Gradle)中添加 Spring WebFlux 依赖。以下是 Maven 和 Gradle 的依赖配置示例:

Maven:

<dependencies>
    <!-- Spring Boot WebFlux -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
</dependencies>

Gradle:

dependencies {
    // Spring Boot WebFlux
    implementation 'org.springframework.boot:spring-boot-starter-webflux'
}

添加 spring-boot-starter-webflux 依赖后,Spring Boot 会自动配置 WebFlux 相关的组件,并启用响应式编程和非阻塞 I/O 功能。

  1. 配置:
    通常情况下,Spring Boot 会自动配置 WebFlux 以及与之相关的组件,因此您无需进行额外的配置。但是,如果需要,您可以在项目的 application.properties 或 application.yml 文件中进行自定义配置。以下是一些常见的 WebFlux 配置项:
  • server.port:设置应用程序的 HTTP 端口,默认值为 8080。
  • spring.codec.max-in-memory-size:设置解码器的最大内存大小,默认值为 2MB。
  • spring.webflux.base-path:设置 WebFlux 应用程序的基本路径。
    例如,在 application.properties 文件中,您可以设置 HTTP 端口:
server.port=8081

开始编码:

1、首先,创建一个表示书籍的 Book 类:

public class Book {
    private String id;
    private String title;
    private String author;

    // 构造函数、getters 和 setters 省略
}

2、接下来,创建一个 BookRepository 接口,用于存储和检索书籍对象:

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

public interface BookRepository {
    Flux<Book> findAll();
    Mono<Book> findById(String id);
    Mono<Book> save(Book book);
    Mono<Void> deleteById(String id);
}

3、现在,创建一个实现 BookRepository 接口的 InMemoryBookRepository 类:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

public class InMemoryBookRepository implements BookRepository {
    private final Map<String, Book> books = new ConcurrentHashMap<>();

    @Override
    public Flux<Book> findAll() {
        return Flux.fromIterable(books.values());
    }

    @Override
    public Mono<Book> findById(String id) {
        return Mono.justOrEmpty(books.get(id));
    }

    @Override
    public Mono<Book> save(Book book) {
        books.put(book.getId(), book);
        return Mono.just(book);
    }

    @Override
    public Mono<Void> deleteById(String id) {
        books.remove(id);
        return Mono.empty();
    }
}

4、接下来,创建一个 BookHandler 类,用于处理书籍相关的 HTTP 请求:

import org.springframework.http.HttpStatus;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerRequest;
import org.springframework.web.reactive.function.server.ServerResponse;
import reactor.core.publisher.Mono;

public class BookHandler {
    private final BookRepository bookRepository;

    public BookHandler(BookRepository bookRepository) {
        this.bookRepository = bookRepository;
    }

    public Mono<ServerResponse> getAllBooks(ServerRequest request) {
        return ServerResponse.ok().body(bookRepository.findAll(), Book.class);
    }

    public Mono<ServerResponse> getBookById(ServerRequest request) {
        return bookRepository.findById(request.pathVariable("id"))
                .flatMap(book -> ServerResponse.ok().body(BodyInserters.fromValue(book)))
                .switchIfEmpty(ServerResponse.notFound().build());
    }

    public Mono<ServerResponse> saveBook(ServerRequest request) {
        Mono<Book> bookMono = request.bodyToMono(Book.class);
        return bookMono.flatMap(book ->
                ServerResponse.status(HttpStatus.CREATED).body(bookRepository.save(book), Book.class));
    }

    public Mono<ServerResponse> deleteBookById(ServerRequest request) {
        return bookRepository.deleteById(request.pathVariable("id"))
                .then(ServerResponse.noContent().build());
    }
}

5、最后,创建一个 BookRouter 类,用于定义书籍相关的 HTTP 路由规则:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.reactive.function.server.RouterFunction;
import org.springframework.web.reactive.function.server.RouterFunctions;
import org.springframework.web.reactive.function.server.ServerResponse;

@Configuration
public class BookRouter {

    @Bean
    public RouterFunction<ServerResponse> route(BookHandler bookHandler) {
        return RouterFunctions
                .route(GET("/books").and(accept(MediaType.APPLICATION_JSON)), bookHandler::getAllBooks)
                .andRoute(GET("/books/{id}")
                    .and(accept(MediaType.APPLICATION_JSON)), bookHandler::getBookById)
                .andRoute(POST("/books").and(accept(MediaType.APPLICATION_JSON)), bookHandler::saveBook)
                .andRoute(DELETE("/books/{id}").and(accept(MediaType.APPLICATION_JSON)), bookHandler::deleteBookById);
    }
}

6、现在,您需要配置一个主应用程序类来启动 Spring Boot 应用程序:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class WebFluxDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(WebFluxDemoApplication.class, args);
    }

    @Bean
    public BookRepository bookRepository() {
        return new InMemoryBookRepository();
    }

    @Bean
    public BookHandler bookHandler(BookRepository bookRepository) {
        return new BookHandler(bookRepository);
    }
}

这个示例演示了如何使用 Spring WebFlux 创建一个简单的 RESTful API。BookHandler 类负责处理 HTTP 请求,BookRouter 类负责定义路由规则,BookRepository 接口负责存储和检索书籍对象。请注意,这个示例使用了响应式编程和非阻塞 I/O,使得应用程序在高并发场景下能够更好地利用系统资源。

拓展与思考

WebFlux 更适合什么类型的项目:

  1. 高并发场景:WebFlux 使用非阻塞 I/O 和响应式编程,使得在高并发场景下能够更好地利用系统资源。对于访问量较大、需要支持大量并发请求的 Web 应用程序,WebFlux 是一个很好的选择。

  2. 微服务架构:在微服务架构中,服务之间的调用和通信非常重要。WebFlux 支持异步和非阻塞的请求处理,可以有效地减小服务间通信的延迟,提高整个系统的响应能力。

  3. 数据流处理:对于需要实时处理数据流的应用程序,WebFlux 提供了响应式编程模型,使得开发者能够更方便地组合、转换和订阅数据流,提高数据处理效率。

WebFlux 存在的一些缺点:

  1. 学习曲线:对于习惯于传统阻塞式编程的开发者来说,响应式编程和非阻塞 I/O 模型可能需要一定时间去学习和适应。尤其是在错误处理、调试和性能优化方面,WebFlux 可能比传统的 Spring MVC 更具挑战性。

  2. 第三方库支持:虽然 WebFlux 已经相对成熟,但与传统的 Spring MVC 相比,一些第三方库可能还没有完全适配响应式编程和非阻塞 I/O 模型。在使用这些库时,可能需要额外的工作来确保与 WebFlux 的兼容性。

  3. 适用性:对于一些低并发、简单的 CRUD 类型的应用程序,使用 WebFlux 可能并不会带来显著的性能提升。在这种情况下,采用更熟悉的 Spring MVC 可能是一个更合适的选择。

总之,WebFlux 更适合处理高并发、数据流处理和微服务架构的场景,但对于一些简单的应用程序,传统的 Spring MVC 可能仍然是一个不错的选择。在决定使用 WebFlux 时,开发者需要权衡项目的需求、团队的技能和第三方库的支持情况。

webFlux 的同类竞品技术对比

在 Java 生态系统中,有一些与 WebFlux 类似的技术,它们都支持响应式编程和非阻塞 I/O。以下是一些 WebFlux 的同类竞品技术:

  1. Vert.x:Vert.x 是一个用于构建响应式应用程序的工具包。它提供了一套简单、可扩展的 API,支持异步和非阻塞 I/O。Vert.x 可以与多种语言(如 Java、JavaScript、Groovy、Ruby、Kotlin 等)一起使用,具有良好的性能和可扩展性。

  2. Akka HTTP:Akka HTTP 是基于 Akka Actors 和 Akka Streams 构建的一个高性能、非阻塞 HTTP 服务器和客户端库。它支持响应式流处理,可以用于构建高并发、低延迟的分布式系统。Akka HTTP 主要使用 Scala 语言编写,但也支持 Java。

  3. Play Framework:Play 是一个支持 Java 和 Scala 的高性能 Web 开发框架。它提供了一套简洁、优雅的 API,支持非阻塞 I/O 和响应式编程。Play 使用 Akka 作为底层基础设施,具有良好的性能和可扩展性。

  4. Micronaut:Micronaut 是一个用于构建微服务和 Serverless 应用程序的现代框架。它提供了响应式编程支持,并与多种响应式库(如 RxJava、Reactor 等)集成。Micronaut 旨在提供高性能、低内存占用的应用程序。

  5. Quarkus:Quarkus 是一个用于构建云原生、微服务和 Serverless 应用程序的全栈框架。它提供了响应式编程支持,并与 Vert.x 集成。Quarkus 优化了启动时间和内存占用,适用于构建高性能、资源高效的应用程序。

技术语言支持特点
Spring WebFluxJava响应式编程,基于 Reactor 库,与 Spring 生态系统紧密集成
Vert.xJava, JavaScript, Groovy, Ruby, Kotlin等异步、非阻塞 I/O,高性能,多语言支持,简洁易用的 API
Akka HTTPScala, Java高性能,基于 Akka Actors 和 Akka Streams,响应式流处理
Play FrameworkJava, Scala高性能,支持非阻塞 I/O 和响应式编程,基于 Akka,优雅的 API
MicronautJava, Groovy, Kotlin响应式编程,高性能,低内存占用,适用于微服务和 Serverless 应用
QuarkusJava, Kotlin响应式编程,高性能,优化启动时间和内存占用,适用于云原生应用

这些技术都有各自的优势和特点。在选择适合项目的技术时,需要考虑性能、可扩展性、易用性、生态系统以及团队的技能和经验等因素。

小结

综上所述,从 Servlet 到 Spring MVC 再到 WebFlux,这个技术升级迭代过程主要体现了对 Web 应用程序开发的简化、性能优化和异步非阻塞编程模型的需求。每个新技术的出现都旨在解决上一个技术的痛点,并为开发者提供更好的工具和实践。

Servlet 的痛点在于开发者需要手动处理请求和响应对象,编写大量样板代码,以及难以管理和组织大型项目的代码结构。Spring MVC 解决了这些问题,为开发者提供了一套简化 Web 开发的工具和约定,同时引入了依赖注入和控制反转功能,降低了开发复杂度。

然而,随着 Web 应用程序的规模和性能需求不断提高,Spring MVC 基于阻塞 I/O 的架构逐渐暴露出性能瓶颈。为了解决这个问题,WebFlux 应运而生。WebFlux 基于响应式编程模型,提供了非阻塞、事件驱动的方式来构建 Web 应用程序。它支持异步和非阻塞 I/O,能够在高并发场景下更好地利用系统资源。同时,WebFlux 提供了与响应式数据存储的集成,实现端到端的响应式编程。

总之,从 Servlet 到 Spring MVC 再到 WebFlux,技术的升级迭代过程反映了 Web 应用程序的发展和性能需求。每个新技术的出现都为开发者提供了更好的工具和实践,以满足不断变化的应用场景。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/414627.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Go的IO -- Go语言设计与实现

Go合IO的不解之缘 协程是Go的很大的一个优势。Go天然支持高并发&#xff0c;那么我们来研究一下这个高并发的秘诀在哪里&#xff1f; 执行体调度得当。CPU 不停的在不同的执行体&#xff08; Goroutine &#xff09;之间反复横跳&#xff01;CPU 一直在装填和运行不同执行体的…

数字化坚鹏:金融数据治理、数据安全政策解读及银行数字化转型

金融数据治理、数据安全政策解读及银行数字化转型课程背景&#xff1a; 很多银行存在以下问题&#xff1a; 不知道如何准确理解金融数据治理及数据安全相关政策 不清楚金融数据治理及数据安全相关政策对银行有什么影响&#xff1f; 不清楚如何进行银行数字化转型&#xff1f…

Azure DevOps Pipelines

Azure DevOps主要通过管理代码、管理服务器、管理发布的管道来实现一体化解决方案 发布流程&#xff1a; 1、代码上传Repos仓储 略 2、DevOps连接并管理发布服务器 2.1、Deployment Groups配置 2.2、服务器执行连接指令 2.3、服务器状态查看 3、创建 Pipline(构建代码) 3.1…

前端中font的使用

知识点&#xff1a; 运行截图&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head> <meta charset"UTF-8"> <meta http-equiv"X-UA-Compatible" content"IEedge"> <meta name&…

【RabbitMQ】SpringBoot整合RabbitMQ、实现RabbitMQ五大工作模式(万字长文)

目录 一、准备 1、创建SpringBoot项目 2、添加配置信息 3、创建配置类 二、RabbitMQ的配置类里创建队列 三、RabbitMQ的配置类里创建交换机及绑定队列 四、SpringBoot整合RabbitMQ入门案例 1、生产者 2、消费者 四、SpringBoot里实现RabbitMQ五大工作模式 1、简单模式…

Linux--进程多线程(上)

前言 精神内耗一方面可能是消极的&#xff0c;人好像一直在跟自己过不去&#xff0c;但其实它也是一种积极的情绪。精神内耗在某种程度上&#xff0c;是在寻找一种出口&#xff0c;寻找他自己人生的出口&#xff0c;寻找我今天的出口&#xff0c;或者寻找我一觉醒来明天的出口。…

【k8s完整实战教程5】网络服务配置(nodeport/loadbalancer/ingress)

系列文章&#xff1a;这个系列已完结&#xff0c;如对您有帮助&#xff0c;求点赞收藏评论。 读者寄语&#xff1a;再小的帆&#xff0c;也能远航&#xff01; 【k8s完整实战教程0】前言【k8s完整实战教程1】源码管理-Coding【k8s完整实战教程2】腾讯云搭建k8s托管集群【k8s完…

恐怖的ChatGPT!

大家好&#xff0c;我是飞哥&#xff01;不知道大家那边咋样。反正我最近感觉是快被ChatGPT包围了。打开手机也全是ChatGPT相关的信息&#xff0c;我的好几个老同学都在问我ChatGPT怎么用&#xff0c;部门内也在尝试用ChatGPT做一点新业务出来。那就干脆我就趁清明假期这一天宝…

AB测试基本原理

AB测试基本原理AB测试AB测试的基本步骤1、AB测试的基本步骤①选取指标指标的分类②建立假设③选取实验单位④计算样本量⑤流量分割⑥实验周期计算⑦线上验证⑧数据检验AB测试 所谓的AB测试就是使用实验组和对照组&#xff0c;通过控制变量法保证实验组和对照组基本条件一致&am…

NumPy 数组学习手册:6~7

原文&#xff1a;Learning NumPy Array 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 六、性能分析&#xff0c;调试和测试 分析&#xff0c;调试和测试是开发过程的组成部分。 您可能熟悉单元测试的概念。 单元测试是程序员编写的用于测试其代码的自动测试。 例如&…

AI —— 一看就懂的代码助手Copilot获取教程

背景 随着chatgpt的发布&#xff0c;人工智能领域近期站上了风口浪尖。GitHub Copilot由github与 OpenAI 合作创建&#xff0c;是世界上第一个使用 OpenAI 的 Codex 模型&#xff08;GPT-3 的后代&#xff09;制作的大规模生成式 AI 开发工具。GitHub Copilot 作为 AI 结对程序…

【条件判断】

目录知识框架No.0 筑基No.1 条件判断题目来源&#xff1a;PTA-L1-031 到底是不是太胖了题目来源&#xff1a;PTA-L1-063 吃鱼还是吃肉题目来源&#xff1a;PTA-L1-069 胎压监测题目来源&#xff1a;PTA-L1-077 大笨钟的心情题目来源&#xff1a;PTA-L1-083 谁能进图书馆知识框架…

Day15-二维数组字符串

文章目录一 二维数组二 字符串案例1案例2案例3-随堂练习案例4-输入-类型转换案例5案例6案例7一 二维数组 <script>// 书:编号 名称 描述 价格/*** 二维数组* */let arr [ [1001,"HTML","网页设计",100],[1002,"CSS","样式设计"…

Leetcode刷题之环形链表

莫等闲&#xff0c;白了少年头&#xff0c;空悲切。 --岳飞 目录 1.环形链表 2.环形链表Ⅱ 1.环形链表 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next …

Stable Diffusion扩散模型

1 GAN到Stable Diffusion的改朝换代 随着人工智能在图像生成&#xff0c;文本生成以及多模态生成等生成领域的技术不断累积&#xff0c;生成对抗网络&#xff08;GAN&#xff09;、变微分自动编码器&#xff08;VAE&#xff09;、normalizing flow models、自回归模型&#xf…

Android Textview Button 等基础组件学习

一 Textview 1 基本使用 <?xml version"1.0" encoding"utf-8"?><LinearLayout android:layout_height"match_parent"android:layout_width"match_parent"android:orientation"vertical"xmlns:android"http…

vue中的pinia使用和持久化 - 粘贴即用

学习关键语句&#xff1a; pinia怎么用 写在前面 pinia 作为 vuex 的替代品好像变得不得不学习了&#xff0c;学起来一用发现 vuex 是什么麻烦的东西&#xff0c;我不认识 这篇文章一共包含的内容有&#xff1a; 安装 pinia读取数据修改数据数据持久化 其中&#xff0c;修…

代码不熟没关系,让AI替你写

程序员早已不是一个陌生的群体&#xff0c;但程序、代码相对普通人而言&#xff0c;看着还是比较深奥难懂&#xff0c;但自从有了ChatGPT&#xff0c;不少对此有兴趣的外行人士&#xff0c;也能轻松写出代码了&#xff0c;比如让ChatGPT写一个贪吃蛇游戏&#xff0c;按它给出的…

C++入门(2)

C入门1.缺省参数1.1. 缺省参数举例和概念1.2. 函数的传参是从左到右给参数的1.3.缺省参数分类1.4. 缺省参数的函数声明与定义2.函数重载2.1.函数重载的概念2.2. 函数重载的情况2.3.剖析C语言不能函数重载而C却可以的原因2.3.1. 编译链接过程2.3.2. 函数名修饰规则3.引用3.1. 引…

Java并行流:一次解决多线程编程难题,让你的程序飞起来

前言 在日常的工作中&#xff0c;为了提高程序的处理速度&#xff0c;充分利用多核处理器的性能&#xff0c;我们需要手动编写多线程代码。但是多线程编程非常复杂&#xff0c;容易出现死锁、竞态条件等问题&#xff0c;给我们带来了很大的困扰。而 Java 并行流则提供了一种更加…