The evolution of Java Web technology: from Servlet, SpringMVC to WebFlux

The evolution of Java Web technology: from Servlet, SpringMVC to WebFlux

Preface
With the rapid development of Internet technology, Web applications face huge challenges when handling massive user access and big data. In this process, Java Web development technology has experienced an evolution from Servlet to Spring MVC to WebFlux. In this article, we will explore the development history, pain points and solutions of these three technologies, and how they can help developers build more efficient, scalable and responsive web applications in the ever-changing Internet world. Let us start from the birth of Servlet and trace the development of Java Web development technology.


From Servlet to Spring MVC to WebFlux, the iterative process of upgrading this technology mainly reflects the development and performance needs of web applications. Below I will introduce the technology at each stage and its pain point solutions in detail.

Servlet:

Servlets are the foundation of Java Web applications and provide a standard Java interface for handling HTTP requests. Servlet is an object-oriented programming model, and each Servlet class handles a specific request. In the early days of web development, servlets were a mainstream solution.

Pain points:
Developers need to manually handle request and response objects and write a lot of boilerplate code.

It is not easy to manage and organize the code structure of large projects.

Spring MVC:

In order to solve the pain points of Servlets, Spring MVC appeared. It is a Java-based web application framework that can be considered a servlet framework, providing a set of tools and conventions that simplify web development. By using Spring MVC, developers can focus more on business logic rather than low-level details.

Improve:
Provides an annotation-based programming model, reducing development complexity.

Provides powerful dependency injection and control inversion functions, simplifying decoupling between components.

Supports flexible URL mapping and view parsing to facilitate developers to manage URLs and views.

Pain points:
Spring MVC is based on blocking I/O, which means that you may encounter performance bottlenecks when handling high concurrent requests.
With the rise of reactive programming models, traditional Spring MVC no longer meets the performance requirements of modern web applications.

WebFlux:

In order to solve the performance bottleneck in Spring MVC, Spring Framework 5.0 introduced WebFlux. WebFlux is based on the reactive programming model, which provides a non-blocking, event-driven way to build web applications.

Improve:
Supports asynchronous and non-blocking I/O, making better use of system resources in high-concurrency scenarios.

Two programming models are provided: annotation-based programming model and function-based programming model.

Provides WebClient, a responsive HTTP client that can replace RestTemplate.

Supports integration with reactive data stores (such as MongoDB, Cassandra, Redis, etc.) to achieve end-to-end reactive programming.

Why does SpringMVC based on blocking I/O mean that it may encounter performance bottlenecks when processing high concurrent requests?
The reason why Spring MVC is based on blocking I/O is that it uses Servlet technology, which by default adopts a model of one thread processing one request. In this model, when a request arrives at the server, the server allocates a thread to handle the request. During the processing, the thread will block and wait for I/O operations (such as database queries, file reading and writing, etc.) to complete before continuing to perform subsequent operations. During an I/O operation, the thread is in a waiting state and cannot handle other requests.

When faced with high concurrent requests, the blocking I/O model may encounter performance bottlenecks for the following reasons:

Limited thread resources: The server's thread resources are limited. When the number of concurrent requests is high, the server may exhaust available threads, preventing new requests from being processed in a timely manner. At the same time, since each thread consumes certain memory and CPU resources, a large number of threads may cause system resource constraints and further reduce performance.

Thread switching overhead: When a large number of requests arrive at the server at the same time, the operating system needs to switch between different threads to ensure that each request can be processed. Thread switching consumes CPU resources and reduces the overall efficiency of the system.

Inefficient I/O operations: In the blocking I/O model, threads cannot handle other requests while waiting for the I/O operation to complete. This means that threads may be waiting most of the time, not fully utilizing CPU resources.

To solve these performance bottlenecks, non-blocking I/O and reactive programming models emerged. In the non-blocking I/O model, a thread does not wait for the I/O operation to complete after initiating it, but returns immediately to handle other requests. When the I/O operation is completed, the thread continues processing through callbacks or events. This model makes full use of thread resources and improves the throughput and responsiveness of the system. WebFlux is a web framework based on this non-blocking I/O and reactive programming model.

What is the principle behind WebFlux's non-blocking and asynchronous implementation?
WebFlux is a framework based on the reactive programming model, which is a new feature introduced in Spring Framework 5.0. WebFlux provides a non-blocking, event-driven way to build reactive web applications. It supports asynchronous processing processes and can better utilize system resources, thereby improving application performance and responsiveness in high-concurrency scenarios.

The principle of WebFlux's non-blocking and asynchronous implementation mainly relies on the following key technologies and concepts:

Reactive programming model: The reactive programming model allows developers to handle data streams and event-driven operations in a declarative manner. In reactive programming, developers can combine, transform, and subscribe to data streams without having to worry about underlying threading and concurrency issues. WebFlux implements reactive programming using the Reactor library, a Java-based reactive programming library that implements the Reactive Streams specification.

Reactive Streams: Reactive Streams is a set of standard interfaces for processing asynchronous data streams, including Publisher, Subscriber, Subscription and Processor. WebFlux uses Reactive Streams to provide a non-blocking streaming data processing mechanism that can improve system throughput and responsiveness in high-concurrency scenarios.

Non-blocking I/O: WebFlux uses Netty as the underlying network communication framework, and Netty supports non-blocking I/O operations. In the non-blocking I/O model, when an I/O operation is initiated, the thread does not block waiting for the operation to complete, but returns immediately and continues processing other tasks. When the I/O operation is completed, the thread will be notified through events or callbacks to continue processing subsequent operations. This mode makes full use of thread resources and improves the throughput and responsiveness of the system.

Asynchronous programming: WebFlux's asynchronous programming is mainly reflected in its support for asynchronous I/O operations and event-driven. WebFlux enables efficient asynchronous request processing by using a reactive programming model and non-blocking I/O. In this model, threads can handle other requests while waiting for I/O operations, thus fully utilizing system resources.

To sum up, the principle of WebFlux's non-blocking and asynchronous implementation mainly relies on the reactive programming model, Reactive Streams specification, non-blocking I/O technology and asynchronous programming. These technologies and concepts work together to enable WebFlux to provide high performance and responsiveness in high-concurrency scenarios.

A Demo developed using webflux
The following is a simple example developed using Spring WebFlux that demonstrates creating a basic RESTful API for managing a simple book list.

Preparation:

Integrating Spring WebFlux in a Spring Boot project is very simple. Here are the steps to integrate WebFlux:

Add dependencies:
First, you need to add the Spring WebFlux dependency in your project's pom.xml (if you are using Maven) or build.gradle (if you are using Gradle). The following is an example dependency configuration for Maven and 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'
}
After adding the spring-boot-starter-webflux dependency, Spring Boot will automatically configure WebFlux-related components and enable reactive programming and non-blocking I/O functions.

Configuration:
Normally, Spring Boot automatically configures WebFlux and its related components, so you don't need to do additional configuration. However, if needed, you can make custom configurations in your project's application.properties or application.yml files. The following are some common WebFlux configuration items:

server.port: Set the HTTP port of the application, the default value is 8080.

spring.codec.max-in-memory-size: Set the maximum memory size of the decoder, the default value is 2MB.

spring.webflux.base-path: Set the base path of the WebFlux application.
For example, in the application.properties file, you can set the HTTP port:

server.port=8081
Start coding:

1. First, create a Book class that represents books:

public class Book {<!-- --> private String id; private String title; private String author; // Constructor, getters and setters omitted}
2. Next, create a BookRepository interface for storing and retrieving book objects:

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. Now, create an InMemoryBookRepository class that implements the BookRepository interface:

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. Next, create a BookHandler class to handle book-related HTTP requests:

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. Finally, create a BookRouter class to define book-related HTTP routing rules:

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;@Configurationpublic 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. Now, you need to configure a main application class to start the Spring Boot application:

import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.context.annotation.Bean;@SpringBootApplicationpublic 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); }}
This example demonstrates how to create a simple RESTful API using Spring WebFlux. The BookHandler class is responsible for handling HTTP requests, the BookRouter class is responsible for defining routing rules, and the BookRepository interface is responsible for storing and retrieving book objects. Note that this example uses reactive programming and non-blocking I/O, allowing the application to better utilize system resources in high-concurrency scenarios.

Expand and think
What type of projects is WebFlux more suitable for:
High concurrency scenarios: WebFlux uses non-blocking I/O and reactive programming to better utilize system resources in high concurrency scenarios. WebFlux is a good choice for web applications that have high traffic and need to support a large number of concurrent requests.

Microservice architecture: In microservice architecture, the invocation and communication between services are very important. WebFlux supports asynchronous and non-blocking request processing, which can effectively reduce the communication delay between services and improve the responsiveness of the entire system.

Data stream processing: For applications that need to process data streams in real time, WebFlux provides a reactive programming model, allowing developers to more easily combine, transform and subscribe to data streams, improving data processing efficiency.

Some disadvantages of WebFlux:
Learning curve: For developers accustomed to traditional blocking programming, reactive programming and non-blocking I/O models may take some time to learn and adapt. Especially in terms of error handling, debugging, and performance optimization, WebFlux can be more challenging than traditional Spring MVC.

Third-party library support: Although WebFlux is relatively mature, some third-party libraries may not be fully adapted to reactive programming and non-blocking I/O models compared with traditional Spring MVC. When using these libraries, additional work may be required to ensure compatibility with WebFlux.

Applicability: For some low-concurrency, simple CRUD-type applications, using WebFlux may not bring significant performance improvements. In this case, adopting the more familiar Spring MVC may be a more appropriate choice.

In short, WebFlux is more suitable for handling high concurrency, data flow processing and microservice architecture scenarios, but for some simple applications, traditional Spring MVC may still be a good choice. When deciding to use WebFlux, developers need to weigh the needs of the project, the skills of the team, and the support of third-party libraries.

Technical comparison of webFlux’s similar competing products
In the Java ecosystem, there are technologies similar to WebFlux that support reactive programming and non-blocking I/O. The following are some of WebFlux’s competing technologies:

Vert.x: Vert.x is a toolkit for building responsive applications. It provides a simple, extensible API that supports asynchronous and non-blocking I/O. Vert.x can be used with multiple languages such as Java, JavaScript, Groovy, Ruby, Kotlin, etc., with good performance and scalability.

Akka HTTP: Akka HTTP is a high-performance, non-blocking HTTP server and client library built on Akka Actors and Akka Streams. It supports reactive stream processing and can be used to build high-concurrency, low-latency distributed systems. Akka HTTP is primarily written in the Scala language, but also supports Java.

Play Framework: Play is a high-performance web development framework that supports Java and Scala. It provides a simple and elegant API that supports non-blocking I/O and reactive programming. Play uses Akka as the underlying infrastructure, which has good performance and scalability.

Micronaut: Micronaut is a modern framework for building microservices and serverless applications. It provides reactive programming support and integrates with various reactive libraries such as RxJava, Reactor, etc. Micronaut is designed to deliver high-performance, low-memory applications.

Quarkus: Quarkus is a full-stack framework for building cloud-native, microservices, and serverless applications. It provides reactive programming support and integrates with Vert.x. Quarkus optimizes startup time and memory footprint for building high-performance, resource-efficient applications.


Each of these technologies has its own advantages and characteristics. When choosing the right technology for your project, consider factors such as performance, scalability, ease of use, ecosystem, and the skills and experience of your team.

summary
To sum up, from Servlet to Spring MVC to WebFlux, this iterative process of technology upgrades mainly reflects the needs for simplification, performance optimization and asynchronous non-blocking programming models of Web application development. The emergence of each new technology aims to solve the pain points of the previous technology and provide developers with better tools and practices.

The pain points of Servlets are that developers need to manually handle request and response objects, write a lot of boilerplate code, and have a code structure that is difficult to manage and organize for large projects. Spring MVC solves these problems and provides developers with a set of tools and conventions to simplify web development. It also introduces dependency injection and control inversion functions to reduce development complexity.

However, as the scale and performance requirements of web applications continue to increase, Spring MVC's blocking I/O-based architecture gradually exposes performance bottlenecks. To solve this problem, WebFlux came into being. WebFlux is based on the reactive programming model and provides a non-blocking, event-driven way to build web applications. It supports asynchronous and non-blocking I/O, making better use of system resources in high-concurrency scenarios. At the same time, WebFlux provides integration with responsive data storage to achieve end-to-end responsive programming.

In short, from Servlet to Spring MVC to WebFlux, the iterative process of technology upgrades reflects the development and performance needs of web applications. The emergence of each new technology provides developers with better tools and practices to meet changing application scenarios.