Spring WebFlux (reactive)

01.07.2026 | Muallif: Jaxongir a.k.a | Kategoriya: Spring Boot | 20 daqiqa o'qish

Spring WebFlux - Spring’ning reactive, non-blocking web framework’i. Bu mavzu quyidagilarni o‘z ichiga oladi: Mono & Flux, reactive controllers, RouterFunction, Reactive Spring Security, R2DBC, SSE/WebSocket, WebFlux vs Virtual Threads.

Spring rasmiy hujjatlarida WebFlux Spring Framework 5.0'da qo‘shilgani, u fully non-blocking, Reactive Streams back pressure qo‘llashi va Netty kabi serverlarda ishlashi aytiladi. (Home)


1. Reactive programming nima?

Oddiy Spring MVC modeli:

Request keladi
↓
Thread olinadi
↓
DB yoki external API javobini kutadi
↓
Response qaytaradi
↓
Thread bo‘shaydi

Muammo:

Thread kutib turibdi.
Ish qilmayapti, lekin band.

Reactive modelda esa:

Request keladi
↓
Operation boshlanadi
↓
Thread kutib turmaydi
↓
Javob tayyor bo‘lganda callback/event orqali davom etadi

Ya’ni WebFlux’ning asosiy g‘oyasi:

Kamroq thread bilan ko‘proq concurrent request ishlatish

Lekin bu hamma joyda tezroq degani emas. WebFlux faqat chain to‘liq non-blocking bo‘lsa foyda beradi.


2. Spring MVC vs Spring WebFlux

Mezoni

Spring MVC

Spring WebFlux

Model

Blocking

Non-blocking

Server

Tomcat/Servlet

Netty, Undertow, Servlet 3.1+

Thread modeli

Request per thread

Event-loop

Return type

DTO, ResponseEntity

Mono, Flux

DB access

JDBC/JPA

R2DBC, Reactive Mongo

O‘rganish

Osonroq

Murakkabroq

Debug qilish

Osonroq

Qiyinroq

Best use-case

CRUD, enterprise app

High concurrency, streaming, async I/O


3. Mono va Flux

Spring WebFlux Project Reactor ustida qurilgan. Spring hujjatlarida Reactor WebFlux uchun asosiy reactive library ekani, Mono 0..1 element, Flux esa 0..N element bilan ishlashi aytiladi. (Home)

Mono nima?

Mono<T> - bitta yoki nol natija.

Mono<User> = bitta user yoki empty

Misol:

@GetMapping("/{id}")
public Mono<UserDto> getUser(@PathVariable Long id) {
    return userService.findById(id);
}

Bu degani:

Hozir UserDto qaytmayapti.
Kelajakda UserDto yoki empty natija keladi.

Flux nima?

Flux<T> - ko‘p natija yoki nol natija.

Project Reactor API hujjatida Flux 0 dan N gacha element chiqaradigan Reactive Streams Publisher sifatida ta’riflanadi. (projectreactor.io)

@GetMapping
public Flux<UserDto> getUsers() {
    return userService.findAll();
}

Bu:

0, 1, 10, 1000 yoki undan ko‘p UserDto oqimi

4. Oddiy reactive controller

@RestController
@RequestMapping("/api/products")
@RequiredArgsConstructor
public class ProductController {

    private final ProductService productService;

    @GetMapping("/{id}")
    public Mono<ProductResponse> getById(@PathVariable Long id) {
        return productService.getById(id);
    }

    @GetMapping
    public Flux<ProductResponse> getAll() {
        return productService.getAll();
    }

    @PostMapping
    public Mono<ProductResponse> create(@RequestBody Mono<CreateProductRequest> request) {
        return request.flatMap(productService::create);
    }
}

E’tibor bering:

@RequestBody Mono<CreateProductRequest> request

Bu request body ham reactive oqim sifatida olinayotganini bildiradi.

Oddiyroq yozish ham mumkin:

@PostMapping
public Mono<ProductResponse> create(@RequestBody CreateProductRequest request) {
    return productService.create(request);
}

Lekin to‘liq reactive style’da body’ni ham Mono qilish mumkin.


5. Reactive service layer

@Service
@RequiredArgsConstructor
public class ProductService {

    private final ProductRepository productRepository;

    public Mono<ProductResponse> getById(Long id) {
        return productRepository.findById(id)
                .map(this::toResponse)
                .switchIfEmpty(Mono.error(new ProductNotFoundException(id)));
    }

    public Flux<ProductResponse> getAll() {
        return productRepository.findAll()
                .map(this::toResponse);
    }

    public Mono<ProductResponse> create(CreateProductRequest request) {
        Product product = new Product();
        product.setName(request.name());
        product.setPrice(request.price());

        return productRepository.save(product)
                .map(this::toResponse);
    }

    private ProductResponse toResponse(Product product) {
        return new ProductResponse(
                product.getId(),
                product.getName(),
                product.getPrice()
        );
    }
}

Bu yerda muhim joy:

.map(this::toResponse)

Bu oddiy transformatsiya.

.flatMap(...)

esa ichida yana Mono yoki Flux qaytadigan async operation bo‘lsa ishlatiladi.


6. map vs flatMap

map

Oddiy qiymatni boshqa qiymatga aylantiradi.

Mono<User> userMono = userRepository.findById(1L);

Mono<UserDto> dtoMono = userMono.map(user -> new UserDto(user.getName()));
User → UserDto

flatMap

Ichida yana reactive operation bo‘lsa ishlatiladi.

Mono<OrderResponse> result = userRepository.findById(userId)
        .flatMap(user -> orderRepository.findByUserId(user.getId()))
        .map(order -> new OrderResponse(order.getId()));
User → Mono<Order> → OrderResponse

Agar map ishlatilsa, natija noto‘g‘ri bo‘ladi:

Mono<Mono<Order>> // yomon

flatMap uni tekislaydi:

Mono<Order> // yaxshi

7. Back pressure

Back pressure - consumer qancha data qabul qila olsa, producer shuncha yuborishi.

Masalan:

Producer: sekundiga 10 000 event chiqaradi
Consumer: sekundiga 1 000 event ishlay oladi

Agar back pressure bo‘lmasa:

Memory to‘lib ketadi
Queue kattalashadi
App sekinlashadi yoki yiqiladi

Reactive Streams’da consumer producer’ga shunday signal beradi:

Menga hozir 100 ta element yubor.
Keyin yana so‘rayman.

Spring WebFlux’ning muhim kuchi shunda: u non-blocking back pressure modelini qo‘llaydi. (Home)


8. RouterFunction va functional endpoints

WebFlux’da endpoint yozishning 2 usuli bor:

1. Annotation-based: @RestController
2. Functional style: RouterFunction + HandlerFunction

Spring hujjatlarida RouterFunction @RequestMappingga o‘xshashi, lekin faqat mapping emas, behavior ham berishi aytiladi. (Home)


RouterFunction misol

@Configuration
public class ProductRouter {

    @Bean
    public RouterFunction<ServerResponse> productRoutes(ProductHandler handler) {
        return RouterFunctions.route()
                .GET("/api/products/{id}", handler::getById)
                .GET("/api/products", handler::getAll)
                .POST("/api/products", handler::create)
                .build();
    }
}

Handler:

@Component
@RequiredArgsConstructor
public class ProductHandler {

    private final ProductService productService;

    public Mono<ServerResponse> getById(ServerRequest request) {
        Long id = Long.valueOf(request.pathVariable("id"));

        return productService.getById(id)
                .flatMap(product ->
                        ServerResponse.ok().bodyValue(product)
                );
    }

    public Mono<ServerResponse> getAll(ServerRequest request) {
        return ServerResponse.ok()
                .body(productService.getAll(), ProductResponse.class);
    }

    public Mono<ServerResponse> create(ServerRequest request) {
        return request.bodyToMono(CreateProductRequest.class)
                .flatMap(productService::create)
                .flatMap(product ->
                        ServerResponse.ok().bodyValue(product)
                );
    }
}

Qachon RouterFunction yaxshi?

Holat

Mosligi

Functional style yoqsa

Yaxshi

Lightweight routing kerak bo‘lsa

Yaxshi

Gateway/proxy style endpointlar

Yaxshi

Oddiy CRUD team uchun

@RestController osonroq

Katta team, standard Spring style

@RestController ko‘pincha qulayroq


9. R2DBC: reactive database access

WebFlux ishlatib, lekin ichida JPA/JDBC ishlatsangiz, chain blocking bo‘lib qoladi.

Yomon kombinatsiya:

WebFlux + JPA/JDBC

Sabab:

JPA/JDBC blocking API.
Event-loop thread bloklanadi.

To‘g‘riroq kombinatsiya:

WebFlux + R2DBC
WebFlux + Reactive MongoDB
WebFlux + Reactive Redis
WebFlux + WebClient

Spring Data R2DBC relational database bilan reactive programming’ni birlashtirish uchun ishlatiladi va DatabaseClient kabi yuqori darajali abstraction beradi. (Home)


R2DBC repository misol

@Table("products")
public class Product {

    @Id
    private Long id;
    private String name;
    private BigDecimal price;

    // getters/setters
}
public interface ProductRepository extends ReactiveCrudRepository<Product, Long> {

    Flux<Product> findByNameContainingIgnoreCase(String name);
}

Controller:

@GetMapping("/search")
public Flux<ProductResponse> search(@RequestParam String name) {
    return productRepository.findByNameContainingIgnoreCase(name)
            .map(product -> new ProductResponse(
                    product.getId(),
                    product.getName(),
                    product.getPrice()
            ));
}

10. Reactive transaction

R2DBC’da transaction ham reactive bo‘lishi kerak.

@Service
@RequiredArgsConstructor
public class OrderService {

    private final TransactionalOperator transactionalOperator;
    private final OrderRepository orderRepository;
    private final PaymentRepository paymentRepository;

    public Mono<OrderResponse> createOrder(CreateOrderRequest request) {
        Mono<OrderResponse> flow = orderRepository.save(new Order(request.userId()))
                .flatMap(order ->
                        paymentRepository.save(new Payment(order.getId(), request.amount()))
                                .map(payment -> new OrderResponse(order.getId(), payment.getStatus()))
                );

        return transactionalOperator.transactional(flow);
    }
}

Oddiy @Transactional har doim kutilganidek ishlamaydi, chunki reactive flow keyinroq execute bo‘ladi. Senior darajada TransactionalOperatorni bilish foydali.


11. WebClient

WebFlux’da boshqa service chaqirish uchun WebClient ishlatiladi.

RestTemplate blocking.
WebClient non-blocking.

@Component
@RequiredArgsConstructor
public class PaymentClient {

    private final WebClient webClient;

    public Mono<PaymentResponse> createPayment(PaymentRequest request) {
        return webClient.post()
                .uri("/api/payments")
                .bodyValue(request)
                .retrieve()
                .bodyToMono(PaymentResponse.class);
    }
}

Config:

@Configuration
public class WebClientConfig {

    @Bean
    public WebClient paymentWebClient(WebClient.Builder builder) {
        return builder
                .baseUrl("http://payment-service")
                .build();
    }
}

12. Reactive error handling

Reactive chain’da error tashlash boshqacha ko‘rinadi.

public Mono<ProductResponse> getById(Long id) {
    return productRepository.findById(id)
            .switchIfEmpty(Mono.error(new ProductNotFoundException(id)))
            .map(this::toResponse);
}

Fallback:

public Mono<ProductResponse> getCachedProduct(Long id) {
    return productClient.getProduct(id)
            .onErrorResume(ex -> cacheService.getProductFromCache(id));
}

Default qiymat:

public Mono<BigDecimal> getRating(Long productId) {
    return ratingClient.getRating(productId)
            .onErrorReturn(BigDecimal.ZERO);
}

Retry:

public Mono<PaymentResponse> pay(PaymentRequest request) {
    return paymentClient.createPayment(request)
            .timeout(Duration.ofSeconds(2))
            .retryWhen(Retry.backoff(2, Duration.ofMillis(300)))
            .onErrorResume(ex ->
                    Mono.just(new PaymentResponse("PENDING"))
            );
}

Muhim: payment kabi operatsiyalarda retry faqat idempotency bilan xavfsiz.


13. Reactive Spring Security

WebFlux’da security ham reactive stack bilan ishlaydi.

Servlet stack’dagi asosiy class:

SecurityFilterChain

Reactive stack’da:

SecurityWebFilterChain

Misol:

@Configuration
@EnableWebFluxSecurity
public class SecurityConfig {

    @Bean
    SecurityWebFilterChain securityWebFilterChain(ServerHttpSecurity http) {
        return http
                .csrf(ServerHttpSecurity.CsrfSpec::disable)
                .authorizeExchange(exchange -> exchange
                        .pathMatchers("/api/public/**").permitAll()
                        .pathMatchers("/api/admin/**").hasRole("ADMIN")
                        .anyExchange().authenticated()
                )
                .oauth2ResourceServer(oauth2 ->
                        oauth2.jwt(Customizer.withDefaults())
                )
                .build();
    }
}

MVC’dagi security code’ni to‘g‘ridan-to‘g‘ri WebFlux’ga ko‘chirib bo‘lmaydi. Stack boshqacha.


14. SSE bilan streaming

SSE - Server-Sent Events. Server browser’ga doimiy event oqimi yuboradi.

Masalan:

Order status update
Notification stream
Live dashboard
Crypto price stream
Delivery tracking

WebFlux’da SSE uchun Flux juda mos.

@GetMapping(value = "/events", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<OrderEvent> streamOrderEvents() {
    return Flux.interval(Duration.ofSeconds(1))
            .map(i -> new OrderEvent("ORDER_UPDATED", Instant.now()));
}

Response bitta JSON emas, oqim:

event 1
event 2
event 3
...

15. WebSocket bilan WebFlux

SSE bir tomonlama:

Server → Client

WebSocket ikki tomonlama:

Client ↔ Server

WebSocket real-time chat, trading, multiplayer, live collaboration kabi joylarda ishlatiladi.

Soddalashtirilgan WebSocket handler:

@Component
public class ChatWebSocketHandler implements WebSocketHandler {

    @Override
    public Mono<Void> handle(WebSocketSession session) {
        Flux<WebSocketMessage> messages = session.receive()
                .map(WebSocketMessage::getPayloadAsText)
                .map(text -> "Echo: " + text)
                .map(session::textMessage);

        return session.send(messages);
    }
}

Config:

@Configuration
public class WebSocketConfig {

    @Bean
    public HandlerMapping webSocketMapping(ChatWebSocketHandler handler) {
        Map<String, WebSocketHandler> map = new HashMap<>();
        map.put("/ws/chat", handler);

        SimpleUrlHandlerMapping mapping = new SimpleUrlHandlerMapping();
        mapping.setUrlMap(map);
        mapping.setOrder(10);
        return mapping;
    }
}

16. WebFlux’da eng katta xato: blocking code

WebFlux ichida bular xavfli:

Thread.sleep(1000);
userRepository.findById(id); // JPA blocking
restTemplate.getForObject(...); // blocking
file.readAllBytes(); // blocking I/O

Bunday code event-loop thread’ni bloklaydi.


Blocking code’ni alohida scheduler’ga chiqarish

Ba’zida blocking API’dan qochib bo‘lmaydi. Masalan eski SDK.

public Mono<LegacyResponse> callLegacySdk() {
    return Mono.fromCallable(() -> legacySdk.call())
            .subscribeOn(Schedulers.boundedElastic());
}

Bu event-loop’ni himoya qiladi.

Lekin bu WebFlux’ni "to‘liq reactive" qilmaydi. Faqat blocking qismni alohida thread pool’ga chiqaradi.


17. WebFlux vs Virtual Threads

Java 21'dan keyin savol ko‘p beriladi:

WebFlux kerakmi yoki virtual threads yetadimi?

Virtual Threads modeli

Virtual threads blocking code’ni arzonlashtiradi.

Spring MVC + virtual threads + JDBC

ko‘p CRUD applicationlar uchun juda yaxshi variant bo‘lishi mumkin.

WebFlux modeli

WebFlux esa non-blocking I/O model.

WebFlux + R2DBC + WebClient + Reactive Redis

yuqori concurrency va streaming uchun kuchli.


Taqqoslash

Holat

Yaxshi tanlov

Oddiy CRUD + PostgreSQL/JPA

Spring MVC + Virtual Threads

Existing JPA project

Spring MVC

Ko‘p external API chaqirish

WebFlux yoki MVC + virtual threads

Streaming response

WebFlux

SSE/WebSocket heavy workload

WebFlux

Reactive Mongo/R2DBC ishlatilsa

WebFlux

Team reactive bilmasa

MVC

Debuggability muhim bo‘lsa

MVC

Event-loop va back pressure kerak bo‘lsa

WebFlux


18. Qachon WebFlux ishlatish kerak?

WebFlux yaxshi tanlov:

[+] Ko‘p concurrent connection bor
[+] External API call ko‘p
[+] Streaming kerak
[+] SSE/WebSocket bor
[+] Reactive DB driver ishlatyapsiz
[+] Back pressure kerak
[+] Thread sonini kam tutish muhim

19. Qachon WebFlux ishlatmaslik kerak?

WebFlux shart emas:

[-] Oddiy CRUD application
[-] JPA/Hibernate asosiy persistence bo‘lsa
[-] Team reactive programming bilmasa
[-] Debuggability muhim bo‘lsa
[-] Blocking library’lar ko‘p bo‘lsa
[-] Performance muammo DB query yoki index’da bo‘lsa

Eng katta qoida:

WebFlux performance muammolarni sehrli hal qilmaydi.
Agar bottleneck DB index, yomon query, N+1 yoki noto‘g‘ri architecture bo‘lsa,
WebFlux yordam bermaydi.

20. Real project example

Masalan, notification system:

Client
  ↓
Gateway
  ↓
Notification Service
  ├── WebFlux endpoint
  ├── SSE stream
  ├── Reactive Redis Pub/Sub
  └── MongoDB reactive repository

Endpoint:

@GetMapping(value = "/notifications/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<NotificationEvent> stream(@AuthenticationPrincipal Jwt jwt) {
    String userId = jwt.getSubject();

    return notificationService.streamForUser(userId);
}

Service:

public Flux<NotificationEvent> streamForUser(String userId) {
    return redisNotificationReceiver.receive(userId)
            .map(message -> new NotificationEvent(
                    message.title(),
                    message.body(),
                    Instant.now()
            ));
}

Bu WebFlux uchun mos use-case:

Ko‘p user ochiq connection ushlab turadi.
Server har bir user uchun alohida blocking thread ushlab turmaydi.

21. Minimal project dependencies

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-r2dbc</artifactId>
    </dependency>

    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>r2dbc-postgresql</artifactId>
    </dependency>
</dependencies>

Config:

spring:
  r2dbc:
    url: r2dbc:postgresql://localhost:5432/shop
    username: postgres
    password: postgres

22. Senior interview javob

Agar suhbatda so‘rasa:

Spring WebFlux nima?

Javob:

Spring WebFlux - Spring’ning reactive, non-blocking web framework’i.
U Project Reactor ustida ishlaydi va Mono/Flux tiplari orqali async data oqimlarini boshqaradi.
Spring MVC request-per-thread modeliga yaqin bo‘lsa, WebFlux event-loop va non-blocking I/O modeliga yaqin.

WebFlux ko‘p concurrent connection, streaming, SSE/WebSocket, ko‘p external API call va reactive database driverlar bilan foydali.
Lekin JPA/JDBC kabi blocking stack bilan ishlatilsa, foydasi kamayadi yoki event-loop bloklanadi.

Java 21 virtual threads’dan keyin oddiy CRUD/JPA loyihalarda MVC + virtual threads ko‘pincha sodda va yaxshi tanlov.
WebFlux esa haqiqiy non-blocking chain, back pressure va streaming kerak bo‘lganda kuchli.

23. Qisqa xulosa

Spring WebFlux’ni bitta gap bilan:

WebFlux - ko‘p request’ni kam thread bilan, non-blocking usulda boshqarish uchun.

Eng muhim tushunchalar:

Tushuncha

Ma’nosi

Mono<T>

0 yoki 1 natija

Flux<T>

0 dan N gacha natija

map

Oddiy transformatsiya

flatMap

Async/reactive transformatsiya

Back pressure

Consumer producer’ni nazorat qiladi

R2DBC

Reactive relational DB access

WebClient

Non-blocking HTTP client

SSE

Serverdan clientga event stream

WebSocket

Ikki tomonlama real-time aloqa

Senior darajada asosiy qaror:

WebFlux ishlatishdan oldin savol:
Butun chain non-blockingmi?
Agar yo‘q bo‘lsa, WebFlux shart emas.