π Reactive Spring: The River of Data
Imagine youβre at a sushi restaurant with a conveyor belt. Instead of ordering and waiting, delicious plates flow past you continuously. You grab what you want, when you want. Thatβs Reactive Programming!
π― What Youβll Learn
- Reactive Fundamentals β The new way to handle data
- WebClient β Your smart assistant for talking to other servers
π Chapter 1: Reactive Fundamentals
The Old Way vs. The New Way
Think of ordering pizza:
| Old Way (Blocking) | New Way (Reactive) |
|---|---|
| Call pizza shop | Call pizza shop |
| Wait on phone⦠| Hang up immediately |
| Canβt do anything else | Do other things! |
| Pizza arrives | Get notified when ready |
The old way: You wait and do nothing. The new way: You keep living your life!
πͺ The Two Magic Words: Mono and Flux
Reactive Spring has two special containers:
// Mono = ONE item (or nothing)
Mono<Pizza> onePizza = Mono.just(pizza);
// Flux = MANY items (like a stream)
Flux<Pizza> manyPizzas = Flux.just(
pizza1, pizza2, pizza3
);
Simple memory trick:
- Mono = Mono = One (sounds like βone-oβ)
- Flux = Flux = Flow of many things
π’ The Conveyor Belt Analogy
graph TD A["π Data Source"] --> B["π¦ Mono/Flux"] B --> C["π§ Transform"] C --> D["π― Subscribe"] D --> E["β¨ You Get Data!"]
The conveyor belt has 3 parts:
- Publisher = The kitchen making food
- Operators = Workers adding toppings
- Subscriber = You, eating the food!
π οΈ Your First Reactive Code
// Create a stream of numbers
Flux<Integer> numbers = Flux.just(1, 2, 3);
// Transform each number (double it)
Flux<Integer> doubled = numbers
.map(n -> n * 2);
// Subscribe to get results!
doubled.subscribe(
n -> System.out.println(n)
);
// Output: 2, 4, 6
What happened?
- We created numbers 1, 2, 3
- We doubled each one
- We printed the results
π¨ Common Operators (Your Toolbox)
| Operator | What It Does | Example |
|---|---|---|
map |
Transform each item | Double numbers |
filter |
Keep only matching | Only even numbers |
flatMap |
One-to-many | One user β many orders |
take |
Limit items | First 5 only |
Flux.just(1, 2, 3, 4, 5)
.filter(n -> n % 2 == 0) // Keep even
.map(n -> n * 10) // Multiply
.subscribe(System.out::println);
// Output: 20, 40
β οΈ The Golden Rule
Nothing happens until you subscribe!
// This does NOTHING yet!
Flux<String> lazy = Flux.just("A", "B")
.map(s -> {
System.out.println("Working!");
return s;
});
// NOW it runs!
lazy.subscribe();
Itβs like setting up dominoes. Nothing falls until you push the first one!
π Chapter 2: WebClient
What is WebClient?
WebClient is your smart assistant for calling other servers.
Old way (RestTemplate): You wait for each call. New way (WebClient): You start many calls at once!
ποΈ Creating a WebClient
// Simple creation
WebClient client = WebClient.create();
// With base URL
WebClient client = WebClient.create(
"https://api.example.com"
);
// With more options
WebClient client = WebClient.builder()
.baseUrl("https://api.example.com")
.defaultHeader("API-Key", "secret")
.build();
π Making Your First Call
// GET request - fetch one user
Mono<User> user = client.get()
.uri("/users/123")
.retrieve()
.bodyToMono(User.class);
// GET request - fetch many users
Flux<User> users = client.get()
.uri("/users")
.retrieve()
.bodyToFlux(User.class);
graph TD A["Your App"] -->|GET /users| B["WebClient"] B -->|Request| C["π Server"] C -->|Response| B B -->|Mono/Flux| A
π POST, PUT, DELETE
// POST - Create new user
Mono<User> created = client.post()
.uri("/users")
.bodyValue(newUser)
.retrieve()
.bodyToMono(User.class);
// PUT - Update user
Mono<User> updated = client.put()
.uri("/users/123")
.bodyValue(updatedUser)
.retrieve()
.bodyToMono(User.class);
// DELETE - Remove user
Mono<Void> deleted = client.delete()
.uri("/users/123")
.retrieve()
.bodyToMono(Void.class);
π¨ Handling Errors
Mono<User> user = client.get()
.uri("/users/123")
.retrieve()
.onStatus(
status -> status.is4xxClientError(),
response -> Mono.error(
new UserNotFoundException()
)
)
.bodyToMono(User.class);
Error handling is like a safety net:
- 4xx errors = Something YOU did wrong
- 5xx errors = Something the SERVER did wrong
π― Real Example: Weather App
@Service
public class WeatherService {
private final WebClient client;
public WeatherService() {
this.client = WebClient.create(
"https://api.weather.com"
);
}
public Mono<Weather> getWeather(
String city) {
return client.get()
.uri("/weather?city={city}", city)
.retrieve()
.bodyToMono(Weather.class)
.onErrorReturn(
Weather.unknown()
);
}
}
ποΈ The Power: Multiple Calls at Once!
// Old way: One by one (slow!)
User user = getUser(); // Wait...
Orders orders = getOrders(); // Wait more...
// New way: All at once (fast!)
Mono<User> userMono = getUser();
Mono<Orders> ordersMono = getOrders();
// Combine when both complete
Mono.zip(userMono, ordersMono)
.map(tuple -> new Dashboard(
tuple.getT1(), // User
tuple.getT2() // Orders
));
graph TD A["Start"] --> B["Get User"] A --> C["Get Orders"] B --> D["Wait for Both"] C --> D D --> E["Combine Results"]
π Key Takeaways
Reactive Fundamentals
β Mono = 0 or 1 item β Flux = 0 to many items β Nothing runs until you subscribe β Use operators to transform data
WebClient
β Non-blocking HTTP calls β Returns Mono or Flux β Can run multiple calls in parallel β Built-in error handling
π Quick Reference
// Mono: One item
Mono.just("Hello")
.map(String::toUpperCase)
.subscribe(System.out::println);
// Flux: Many items
Flux.range(1, 5)
.filter(n -> n > 2)
.subscribe(System.out::println);
// WebClient: HTTP call
WebClient.create("https://api.com")
.get()
.uri("/data")
.retrieve()
.bodyToMono(Data.class)
.subscribe(System.out::println);
π Congratulations! You now understand Reactive Spring! Remember: data flows like a river. You donβt block the river β you ride the waves! πββοΈ
