Caching and Async

Back

Loading concept...

🚀 Spring Caching and Async: Your App’s Secret Superpowers

The Story of the Busy Restaurant

Imagine you own a super busy restaurant. Every time a customer asks “What’s today’s soup?”, your chef runs to the kitchen, checks all the ingredients, cooks a tiny sample, tastes it, and comes back to tell them. That takes forever!

What if the chef just wrote it on a board once? Now everyone can see it instantly! That’s caching.

But wait—what if customers also order food that takes 30 minutes to cook? Do they stand at the counter blocking everyone else? No! They sit down, and a waiter brings it when it’s ready. That’s async processing.

Spring gives you these superpowers built right in. Let’s discover them together!


🧠 Part 1: Spring Caching — Remember Things So You Don’t Have to Repeat

What is Caching?

Caching is like having a super memory for your app. Instead of calculating the same thing over and over, you calculate it once and remember the answer.

Real Life Example:

  • Mom asks: “What’s 7 × 8?”
  • First time: You count on fingers… “56!”
  • Second time: “Oh, I remember! 56!”

That’s caching. Your brain cached the answer!

How Spring Caching Works

Spring makes caching ridiculously easy. You just add a magic word (annotation) to your method:

@Cacheable("soups")
public String getTodaysSoup() {
    // This slow code runs only ONCE
    return cookAndTasteSoup();
}

What happens:

  1. First call → runs the method, stores result
  2. Second call → skips method, returns stored result
  3. Third call → still uses stored result!

The Three Magic Annotations

graph TD A["📦 @Cacheable"] --> B["Save result for later"] C["🗑️ @CacheEvict"] --> D["Delete old cached data"] E["🔄 @CachePut"] --> F["Update cached data"]

1. @Cacheable — “Remember This!”

@Cacheable("users")
public User findUser(Long id) {
    // Database call happens once per id
    return userRepository.findById(id);
}

2. @CacheEvict — “Forget This!”

@CacheEvict(value = "users", key = "#id")
public void deleteUser(Long id) {
    // Clear cache when user is deleted
    userRepository.delete(id);
}

3. @CachePut — “Update This!”

@CachePut(value = "users", key = "#user.id")
public User updateUser(User user) {
    // Always runs AND updates cache
    return userRepository.save(user);
}

Enable Caching in Your App

Just add one annotation to your main class:

@SpringBootApplication
@EnableCaching  // ← Magic switch!
public class MyApp {
    public static void main(String[] args) {
        SpringApplication.run(MyApp.class);
    }
}

⏰ Part 2: Task Scheduling — Do Things Automatically

The Alarm Clock for Your Code

Remember how your alarm wakes you up every day at 7 AM? Spring can do the same for your code!

Use cases:

  • Send daily report emails at 9 AM
  • Clean up old files every night
  • Check for updates every 5 minutes

Two Ways to Schedule Tasks

graph TD A["🕐 Scheduling"] --> B["Fixed Rate"] A --> C["Cron Expression"] B --> D["Every X milliseconds"] C --> E["Specific times/dates"]

Method 1: Fixed Rate

@Scheduled(fixedRate = 60000)
public void checkForUpdates() {
    // Runs every 60 seconds (60000 ms)
    System.out.println("Checking updates...");
}

Method 2: Cron Expressions

@Scheduled(cron = "0 0 9 * * MON-FRI")
public void sendDailyReport() {
    // Runs at 9:00 AM, Monday to Friday
    emailService.sendReport();
}

Cron Cheat:

"0 0 9 * * MON-FRI"
 │ │ │ │ │ │
 │ │ │ │ │ └── Day of week
 │ │ │ │ └──── Month
 │ │ │ └────── Day of month
 │ │ └──────── Hour (9 AM)
 │ └────────── Minute (0)
 └──────────── Second (0)

Enable Scheduling

@SpringBootApplication
@EnableScheduling  // ← Turn on the alarm!
public class MyApp { }

🏃 Part 3: Async Processing — Don’t Make People Wait!

The Restaurant Waiter Story

Back to our restaurant! When you order a steak:

Without async (bad):

“Please wait here at the counter for 30 minutes while we cook…”

With async (good):

“Please sit down! We’ll bring it when ready!”

Making Methods Async

@Async
public void sendWelcomeEmail(User user) {
    // This runs in background!
    // Caller doesn't wait
    emailService.send(user.getEmail());
}

What happens when you call it:

graph LR A["Main Thread"] --> B["Calls sendEmail"] B --> C["Returns immediately!"] D["Background Thread"] --> E["Actually sends email"]

Getting Results Back: CompletableFuture

What if you need the result later? Use CompletableFuture:

@Async
public CompletableFuture<Report> generateReport() {
    Report report = heavyCalculation();
    return CompletableFuture
        .completedFuture(report);
}

Using it:

CompletableFuture<Report> future =
    reportService.generateReport();

// Do other stuff while waiting...
doOtherThings();

// Now get the result (waits if not ready)
Report report = future.get();

Enable Async

@SpringBootApplication
@EnableAsync  // ← Background magic!
public class MyApp { }

⚠️ Part 4: The Async Proxy Trap — The Sneaky Bug!

The Mirror Room Mystery

Imagine you’re in a room with a magic mirror. When you tell the mirror to do something, it shows you doing it differently (like adding superpowers). But if you just do it yourself without looking in the mirror, no superpowers!

Spring’s @Async works through proxies (the magic mirror).

The Problem: Self-Invocation

@Service
public class EmailService {

    @Async
    public void sendEmail(String to) {
        // This should be async...
    }

    public void notifyUsers(List<String> users) {
        for (String user : users) {
            sendEmail(user); // 🚨 NOT ASYNC!
        }
    }
}

Why doesn’t it work?

  • When you call sendEmail from inside the same class…
  • You’re NOT going through the proxy (magic mirror)
  • So the @Async magic doesn’t happen!
graph TD A["External Call"] --> B["Goes through Proxy"] B --> C["✅ Async works!"] D["Internal Call"] --> E["Skips Proxy"] E --> F["❌ Async ignored!"]

The Solution: Separate Classes

@Service
public class AsyncEmailSender {
    @Async
    public void sendEmail(String to) {
        // Now truly async!
    }
}

@Service
public class NotificationService {

    @Autowired
    private AsyncEmailSender emailSender;

    public void notifyUsers(List<String> users) {
        for (String user : users) {
            emailSender.sendEmail(user); // ✅ Works!
        }
    }
}

Remember This Rule

Never call @Async methods from within the same class!

The same rule applies to @Cacheable and @Transactional too!


📢 Part 5: Spring Event System — Apps That Talk to Each Other

The School Announcement System

Imagine a school with:

  • A principal who makes announcements
  • Students, teachers, cleaners who all hear them

The principal doesn’t walk to each person. She just speaks into the microphone, and everyone who’s listening hears it!

That’s the Spring Event System!

Three Parts of Events

graph LR A["📣 Publisher"] --> B["📨 Event"] B --> C["👂 Listener 1"] B --> D["👂 Listener 2"] B --> E["👂 Listener 3"]

Step 1: Create an Event

public class UserRegisteredEvent {
    private final User user;

    public UserRegisteredEvent(User user) {
        this.user = user;
    }

    public User getUser() {
        return user;
    }
}

Step 2: Publish the Event

@Service
public class UserService {

    @Autowired
    private ApplicationEventPublisher publisher;

    public void registerUser(User user) {
        userRepository.save(user);

        // 📣 Announce to everyone!
        publisher.publishEvent(
            new UserRegisteredEvent(user)
        );
    }
}

Step 3: Listen for Events

@Component
public class WelcomeEmailListener {

    @EventListener
    public void onUserRegistered(
            UserRegisteredEvent event) {
        // Send welcome email
        emailService.sendWelcome(
            event.getUser()
        );
    }
}

@Component
public class AnalyticsListener {

    @EventListener
    public void onUserRegistered(
            UserRegisteredEvent event) {
        // Track registration
        analytics.track("signup");
    }
}

Make Event Listeners Async

Want listeners to run in background? Easy!

@Component
public class SlowTaskListener {

    @Async
    @EventListener
    public void handleEvent(MyEvent event) {
        // Runs in background thread
        doSlowProcessing();
    }
}

Why Events Are Amazing

  1. Loose Coupling: Publisher doesn’t know about listeners
  2. Easy to Add Features: Just add new listeners!
  3. Clean Code: Each listener does one thing

🎯 Quick Summary

Feature What It Does Enable With
Caching Remember results @EnableCaching
Scheduling Run code on schedule @EnableScheduling
Async Run in background @EnableAsync
Events Components talk without knowing each other Built-in!

The Golden Rules

  1. ✅ Use @Cacheable for expensive operations
  2. ✅ Use @Scheduled for recurring tasks
  3. ✅ Use @Async for long-running operations
  4. ⚠️ Never call @Async from same class
  5. ✅ Use events for decoupled communication

🚀 You Did It!

You just learned four superpowers:

  1. Caching — Your app’s super memory
  2. Scheduling — Your app’s alarm clock
  3. Async — Your app’s multi-tasking power
  4. Events — Your app’s announcement system

These aren’t just features—they’re the secret sauce that makes professional apps fast, reliable, and scalable.

Now go build something amazing! 🌟

Loading story...

Story - Premium Content

Please sign in to view this story and start learning.

Upgrade to Premium to unlock full access to all stories.

Stay Tuned!

Story is coming soon.

Story Preview

Story - Premium Content

Please sign in to view this concept and start learning.

Upgrade to Premium to unlock full access to all content.