Spring Boot Events සිංහලෙන් | ApplicationEvent, @EventListener සමග ඉගෙනගන්න

Spring Boot Events සිංහලෙන් | ApplicationEvent, @EventListener සමග ඉගෙනගන්න

Spring Boot Events: සිදුවීම් කළමනාකරණය පහසුවෙන්

කොහොමද යාලුවනේ? අද අපි කතා කරන්න යන්නේ Spring Boot Applications වල design එක තවත් smart, scalable තැනකට ගෙනියන්න පුලුවන් පට්ට topic එකක් ගැන - ඒ තමයි Spring Boot Events. සාමාන්‍ය ලෝකයේත් අපි "event" නැත්නම් "සිදුවීම්" කියන දේට හැමතිස්සෙම මුහුණ දෙනවා නේද? උදාහරණයක් විදිහට, ක්‍රිකට් මැච් එකක් දිනුවාම ඒක event එකක්, නැත්නම් යාළුවෙක් බඳිනකොට ඒකත් event එකක්. මේ වගේම, අපේ Software Applications ඇතුළෙත් ගොඩක් "සිදුවීම්" වෙනවා. User කෙනෙක් register වෙන එක, order එකක් place කරන එක, password එකක් reset කරන එක... මේ හැම එකක්ම එක එක සිදුවීම් විදිහට අපිට හඳුනාගන්න පුළුවන්.

ඉතින්, මේ සිදුවීම් හරියට කළමනාකරණය කරගන්න අපිට පුළුවන්නම්, අපේ Application එකේ Components අතර තියෙන බැඳීම (coupling) අඩු කරගෙන, maintain කරන්න පහසු, scalable solution එකක් හදාගන්න පුළුවන්. මේක තමයි Event-Driven Architecture කියන්නේ. Spring Boot මේ Event-Driven Architecture එක අපිට ගොඩක් පහසුවෙන් implement කරන්න පුලුවන් විදියට Tools සපයනවා. මේ post එකෙන් අපි බලමු ApplicationEvent සහ @EventListener වගේ දේවල් පාවිච්චි කරලා කොහොමද custom event එකක් publish කරලා, ඒක handle කරන්නේ කියලා. සෙට් වෙන්න එහෙනම්!

Spring Boot Events කියන්නේ මොනවද?

සරලවම කියනවා නම්, Spring Boot Events කියන්නේ Application එකේ Component එකකට තවත් Component එකකට යම් සිදුවීමක් ගැන දැනුම් දෙන්න පුළුවන් යාන්ත්‍රණයක් (mechanism) විතරයි. මේකෙදි ප්‍රධාන players දෙන්නෙක් ඉන්නවා:

  • Publisher (ප්‍රකාශකයා): සිදුවීමක් වෙනකොට ඒක ගැන notify කරන කෙනා.
  • Listener (සවන්දෙන්නා): සිදුවීමක් ගැන notify කරාම ඒකට අදාළ වැඩේ කරන කෙනා.

මේ Publisher සහ Listener අතර direct communication එකක් නැහැ. Publisher දන්නේ නැහැ කවුද listen කරන්නේ කියලා, ඒ වගේම Listener දන්නේ නැහැ කවුද publish කරන්නේ කියලා. මේකෙන් වෙන්නේ Components අතර decoupling එක වැඩි වෙන එක. ඒ කියන්නේ, එක component එකක් වෙනස් කළාම අනිත් එකට ලොකුවට බලපාන්නේ නැහැ.

Spring Framework එකේදී මේ Event Mechanism එකට පදනම වෙන්නේ ApplicationEvent කියන abstract class එක. ඕනෑම custom event එකක් මේ ApplicationEvent class එක extend කරන්න ඕනේ. ඒ වගේම, events publish කරන්න අපිට ApplicationEventPublisher interface එක පාවිච්චි කරන්න පුළුවන්.

Custom Event එකක් හදමු

හරි, දැන් අපි theory ඇති, practical වැඩේට බහිමු. අපි හිතමු අපේ Application එක e-commerce system එකක් කියලා. User කෙනෙක් order එකක් place කරාම අපිට ඒක event එකක් විදිහට publish කරන්න ඕනේ. මේ සඳහා අපි OrderPlacedEvent කියන custom event එක හදමු.

මුලින්ම, ApplicationEvent extend කරන class එකක් හදමු:


// src/main/java/com/example/springbootevents/event/OrderPlacedEvent.java
package com.example.springbootevents.event;

import org.springframework.context.ApplicationEvent;

public class OrderPlacedEvent extends ApplicationEvent {

    private String orderId;
    private String customerEmail;
    private double totalAmount;

    public OrderPlacedEvent(Object source, String orderId, String customerEmail, double totalAmount) {
        super(source); // 'source' is the object on which the event initially occurred
        this.orderId = orderId;
        this.customerEmail = customerEmail;
        this.totalAmount = totalAmount;
    }

    public String getOrderId() {
        return orderId;
    }

    public String getCustomerEmail() {
        return customerEmail;
    }

    public double getTotalAmount() {
        return totalAmount;
    }

    @Override
    public String toString() {
        return "OrderPlacedEvent{" +
               "orderId='" + orderId + '\'' +
               ", customerEmail='" + customerEmail + '\'' +
               ", totalAmount=" + totalAmount +
               '}';
    }
}

මේ OrderPlacedEvent class එකේ අපිට ඕන කරන order details (orderId, customerEmail, totalAmount) අඩංගු වෙනවා. ApplicationEvent constructor එකට source object එකක් යවන්න ඕනේ. ඒක සාමාන්‍යයෙන් event එක publish කරන object එක වෙන්න පුළුවන්. ඒකෙන් Spring Framework එකට event එකේ source එක identify කරන්න පුළුවන්.

Event එකක් Publish කරමු

අපි custom event එක හැදුවා. දැන් බලමු කොහොමද මේ event එක publish කරන්නේ කියලා. මේකට අපි ApplicationEventPublisher interface එක පාවිච්චි කරනවා. මේක Spring IoC Container එකෙන් auto-wire කරගන්න පුළුවන්.

අපි හිතමු අපිට OrderService එකක් තියෙනවා කියලා. Order එකක් place කරාට පස්සේ මේ OrderService එකෙන් event එක publish කරන්න පුළුවන්.


// src/main/java/com/example/springbootevents/service/OrderService.java
package com.example.springbootevents.service;

import com.example.springbootevents.event.OrderPlacedEvent;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    private final ApplicationEventPublisher eventPublisher;

    // Dependency Injection for ApplicationEventPublisher
    public OrderService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public String placeOrder(String customerEmail, double amount) {
        // Here you would typically save the order to a database
        // For demonstration, let's just generate a simple orderId
        String orderId = "ORD-" + System.currentTimeMillis();
        System.out.println("Order " + orderId + " placed successfully for " + customerEmail + " with amount " + amount);

        // Publish the custom event
        eventPublisher.publishEvent(new OrderPlacedEvent(this, orderId, customerEmail, amount));
        System.out.println("OrderPlacedEvent published for order: " + orderId);
        
        return orderId;
    }
}

මේ OrderService එකේ placeOrder method එකෙන් order එකක් place කරලා, ඊට පස්සේ eventPublisher.publishEvent() method එක පාවිච්චි කරලා අපි හදපු OrderPlacedEvent එක publish කරනවා. මේකෙන් වෙන්නේ Spring ApplicationContext එකට මේ event එක ගැන දැනුම් දෙන එක.

Event එකට සවන් දෙමු (@EventListener)

හරි, දැන් event එක publish කරන හැටි අපි දන්නවා. ඊළඟට ඕනේ මේ event එකට "සවන් දෙන" listener එකක් හදන එක. Spring Boot වලදී මේකට පට්ටම පහසු ක්‍රමයක් තියෙනවා - ඒ තමයි @EventListener annotation එක. ඕනෑම Spring Component එකක method එකක් මේ @EventListener වලින් annotate කරලා අපිට Event Listener එකක් හදාගන්න පුළුවන්.

මේ method එකේ parameter එක විදිහට අපි listen කරන්න යන event type එක දෙන්න ඕනේ. අපි හදමු OrderEventHandler කියන class එකක්, ඒකෙන් order එකක් place කරාම customer ට email එකක් යවනවා කියලා හිතමු.


// src/main/java/com/example/springbootevents/listener/OrderEventHandler.java
package com.example.springbootevents.listener;

import com.example.springbootevents.event.OrderPlacedEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

@Component
public class OrderEventHandler {

    @EventListener
    public void handleOrderPlacedEvent(OrderPlacedEvent event) {
        System.out.println("--- Order Placed Event Received ---");
        System.out.println("Order ID: " + event.getOrderId());
        System.out.println("Customer Email: " + event.getCustomerEmail());
        System.out.println("Total Amount: " + event.getTotalAmount());
        
        // Simulate sending an email or updating inventory
        System.out.println("Simulating email to " + event.getCustomerEmail() + " for order " + event.getOrderId());
        // You can add more complex logic here, e.g., calling an email service
    }

    // You can listen for multiple event types or even all ApplicationEvents
    @EventListener
    public void handleAnyApplicationEvent(ApplicationEvent event) {
        // This method will be called for ANY ApplicationEvent published
        // Useful for logging or debugging
        // System.out.println("Received generic Application Event: " + event.getClass().getSimpleName());
    }
}

OrderEventHandler එක Spring Component එකක් (@Component). ඒ නිසා Spring IoC Container එකෙන් මේක manage කරනවා. handleOrderPlacedEvent method එක @EventListener වලින් annotate කරලා තියෙන නිසා, OrderPlacedEvent එකක් publish වුණාම Spring Framework එක auto-magically මේ method එක invoke කරනවා. සිරා වැඩක් නේද?

දැන් අපි බලමු මේක වැඩ කරනවාද කියලා. අපිට පුළුවන් Spring Boot Application එකේ main method එකෙන් OrderService එක call කරලා බලන්න.


// src/main/java/com/example/springbootevents/SpringbootEventsApplication.java
package com.example.springbootevents;

import com.example.springbootevents.service.OrderService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class SpringbootEventsApplication {

    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(SpringbootEventsApplication.class, args);

        // Get the OrderService bean from the context
        OrderService orderService = context.getBean(OrderService.class);

        // Place an order, which will trigger the event
        String orderId = orderService.placeOrder("[email protected]", 150.75);
        System.out.println("Application finished placing order: " + orderId);

        // Close the context when done (optional, for standalone apps)
        // context.close();
    }

}

මේ Application එක run කරාම Console එකේ output එක මේ වගේ වෙයි:


Order ORD-xxxxxxxxxxxx placed successfully for [email protected] with amount 150.75
OrderPlacedEvent published for order: ORD-xxxxxxxxxxxx
--- Order Placed Event Received ---
Order ID: ORD-xxxxxxxxxxxx
Customer Email: [email protected]
Total Amount: 150.75
Simulating email to [email protected] for order ORD-xxxxxxxxxxxx
Application finished placing order: ORD-xxxxxxxxxxxx

ඔයාට පේනවා නේද, OrderService එකෙන් event එක publish කරපු ගමන්, OrderEventHandler එකේ method එක automatically call වෙන හැටි. මේකෙන් අපිට පුළුවන් Order Placing logic එකෙන් email sending වගේ දේවල් decouple කරන්න. ඒ කියන්නේ, order placing logic එකට email sending එක ගැන දැනගන්න අවශ්‍ය නැහැ. Order එක place කරා විතරයි, ඉතුරු ටික Event Listener එක බලාගනියි.

සටහන: සාමාන්‍යයෙන් Spring Events Synchronous (එකම Thread එකේ) විදියට තමයි වැඩ කරන්නේ. ඒ කියන්නේ, event listener එකේ වැඩේ ඉවර වෙනකන් publisher එක block වෙනවා. Async විදියට වැඩ කරන්න ඕනේ නම් @Async annotation එක පාවිච්චි කරන්න පුළුවන්. ඒක ගැන වෙනම post එකකින් කතා කරමු.

ඇයි මේ Event Driven Architecture හොඳ?

දැන් ඔයාට හොඳටම තේරෙනවා ඇති මේ Spring Boot Events කියන දේ කොච්චර වටිනවද කියලා. මේකෙන් අපිට ලැබෙන ප්‍රධානම වාසි ටිකක් තමයි:

  • Decoupling (ශක්තිමත් නොබැඳීම): Components අතර direct dependencies අඩු වෙනවා. එක Component එකක් වෙනස් කරනකොට අනිත් ඒවට බලපාන එක අඩු වෙනවා. මේකෙන් Maintainability සහ Scalability වැඩි වෙනවා.
  • Extensibility (පහසු ව්‍යාප්ත කිරීම): අලුත් features එකතු කරන එක පහසු වෙනවා. උදාහරණයක් විදිහට, OrderPlacedEvent එක publish කරනකොට, අපිට පුළුවන් තව listener එකක් එකතු කරන්න Inventory update කරන්න, Loyalty points add කරන්න වගේ දේවල් වලට. මුල් code එක වෙනස් කරන්න ඕනේ නැහැ.
  • Testability (පහසු Testing): Components එකිනෙකින් ස්වාධීන නිසා, ඒවා වෙන වෙනම test කරන්න පහසුයි.
  • Responsiveness (වේගවත් ප්‍රතිචාර): Async events පාවිච්චි කරනකොට, long-running operations (e.g., sending email) වෙනම thread එකක execute කරලා user ට ඉක්මනින් response එකක් දෙන්න පුළුවන්.

මේ වගේ Event-Driven Architecture එකක්, Microservices වගේ Distributed Systems වලදීත් ගොඩක් වැදගත් වෙනවා. ඒකෙන් Services අතර communication එක වඩාත් effective විදිහට manage කරගන්න පුළුවන්.

නිගමනය

ඉතින් යාලුවනේ, ඔන්න ඔයාලා අද ඉගෙනගත්තා Spring Boot Events පාවිච්චි කරලා කොහොමද Event-Driven Architecture එකක් අපේ Applications වලට implement කරන්නේ කියලා. ApplicationEvent එකක් හදන හැටි, ApplicationEventPublisher එකෙන් events publish කරන හැටි, ඒ වගේම @EventListener පාවිච්චි කරලා ඒවට සවන් දෙන හැටි අපි හොඳටම බැලුවා. මේකෙන් ඔයාගේ Spring Boot Application එක තවත් modular සහ maintain කරන්න පහසු එකක් බවට පත් කරගන්න පුළුවන්.

මේ concepts ගැන තවත් ගැඹුරට හිතලා බලන්න, ඔයාගේ projects වලට මේක incorporate කරන්න උත්සාහ කරන්න. ප්‍රශ්න තියෙනවා නම්, හිතේ තියෙන දේවල් comment section එකේ කියන්න අමතක කරන්න එපා. ඔයාට මේ post එකෙන් අලුත් දෙයක් ඉගෙනගන්න ලැබුණා නම්, යාළුවෝ අතරේ share කරන්නත් අමතක කරන්න එපා! තවත් මේ වගේ පට්ටම topic එකකින් හමුවෙමු. එතකන් හැමෝටම ජය වේවා!