Spring Boot Basics — DI, Controllers & Services

Spring Boot-এর ভিত্তি — DI, Controller ও Service

Read: ~40 min Advanced 5 practice problems Spring concepts

1. Spring in One Idea: Don't Call new. Ask.

Spring's greatest idea is inversion of control. Instead of your code creating its own collaborators (new PaymentGateway()), you declare that you need one, and Spring's application context hands you a ready-made instance. Your classes shrink to pure business logic; wiring moves out of your code entirely.

Spring-এর সবচেয়ে বড় ধারণা — Inversion of Control (IoC)। আপনি নিজে new দিয়ে object তৈরি করেন না; ঘোষণা করেন যে আপনার কী দরকার, আর Spring-এর application context সেটি তৈরি করে হাতে দেয়। আপনার class-গুলোতে শুধু business logic থাকে, wiring কোডের বাইরে চলে যায়।
Sandbox note: Spring is NOT installed in the browser sandbox — install locally with Maven (spring-boot-starter-web) to run these examples. The plain-Java DI demo in §4 below DOES run.

2. A Tiny Spring Boot App

A Spring Boot application is a normal Java main method annotated with @SpringBootApplication. That single annotation turns on component scanning, auto-configuration, and the embedded Tomcat server in one stroke.

Spring Boot app একটি সাধারণ main method, শুধু @SpringBootApplication annotation। এই একটিই annotation chalu করে dial — component scan, auto-configuration, এবং embedded Tomcat server।
HelloApplication.java
package com.abcltech.hello;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.*;

@SpringBootApplication
@RestController
class HelloApplication {

    public static void main(String[] args) {
        SpringApplication.run(HelloApplication.class, args);
    }

    @GetMapping("/hello")
    public String hello(@RequestParam(defaultValue = "World") String name) {
        return "Hello, " + name + "!";
    }
}
Expected (after mvn spring-boot:run):
$ curl http://localhost:8080/hello?name=Bangladesh
Hello, Bangladesh!
AnnotationRoleবাংলায়
@SpringBootApplicationBootstrap — enables scan + autoconfig.পুরো app চালু করে।
@RestControllerClass handles HTTP and returns JSON/text.HTTP request handle করে।
@ServiceBusiness-logic bean.Business logic bean।
@RepositoryData-access bean.Data access bean।
@ComponentGeneric Spring-managed bean.Generic Spring bean।
@AutowiredAsk Spring for a dependency.Spring-এর কাছে dependency চাওয়া।

3. How DI Works — Picture It

Inversion of Control — Spring Wires Your Graph @RestController OrderController needs → OrderService @Service OrderService needs → OrderRepository @Repository OrderRepository JpaRepository Figure 47.1 — Controller Service চায়, Service Repository চায়। Spring পুরো graph নিজে তৈরি করে।
Controller-এর Service দরকার, Service-এর Repository দরকার। আপনি শুধু ঘোষণা করেন; Spring পুরো graph নিজে তৈরি করে সঠিক ক্রমে wiring করে।

4. DI Without Spring — The Idea in Plain Java

The best way to demystify Spring is to do a miniature version by hand. Below, OrderController declares a constructor dependency on OrderService. A tiny "context" class new-s up the graph in the right order and injects collaborators.

Spring-কে demystify করার সেরা উপায় — হাতে ছোট একটি সংস্করণ লেখা। নিচে OrderController-এর constructor-এ OrderService-এর dependency, এবং একটি ছোট Context সঠিক ক্রমে object তৈরি করে wiring করছে।
Main.java
class OrderRepository {
    public int countOrders() { return 42; }
}

class OrderService {
    private final OrderRepository repo;
    OrderService(OrderRepository repo) { this.repo = repo; }           // constructor injection
    public String summary() { return "Orders so far: " + repo.countOrders(); }
}

class OrderController {
    private final OrderService svc;
    OrderController(OrderService svc) { this.svc = svc; }
    public String handleGet() { return "HTTP 200 · " + svc.summary(); }
}

class MiniContext {
    // Simulates a Spring container: build singletons in dependency order
    OrderRepository repo = new OrderRepository();
    OrderService    svc  = new OrderService(repo);
    OrderController ctrl = new OrderController(svc);
}

class Main {
    public static void main(String[] args) {
        MiniContext ctx = new MiniContext();
        System.out.println(ctx.ctrl.handleGet());
    }
}

5. The Same Thing, With Spring

With Spring, you just add annotations. No MiniContext, no manual new.

Spring দিয়ে শুধু annotation যোগ করতে হয় — MiniContext লাগে না, new লাগে না।
OrderController.java
import org.springframework.stereotype.*;
import org.springframework.web.bind.annotation.*;

@Repository
class OrderRepository {
    public int countOrders() { return 42; }
}

@Service
class OrderService {
    private final OrderRepository repo;
    OrderService(OrderRepository repo) { this.repo = repo; }
    public String summary() { return "Orders so far: " + repo.countOrders(); }
}

@RestController
class OrderController {
    private final OrderService svc;
    OrderController(OrderService svc) { this.svc = svc; }
    @GetMapping("/orders/summary")
    public String get() { return svc.summary(); }
}
Expected:
$ curl http://localhost:8080/orders/summary
Orders so far: 42
Always prefer constructor injection. The dependency is final, the object is never in a half-initialized state, and unit tests can pass fakes directly — no Spring required.

সব সময় constructor injection বেছে নিন। Field final হয়, object কখনো অর্ধ-initialised অবস্থায় থাকে না, এবং test-এ সরাসরি fake পাঠানো যায় — Spring ছাড়াই।

6. Configuration: application.properties

Spring Boot reads a plain properties file from src/main/resources/application.properties (or the YAML equivalent). Any key is available via @Value or strongly-typed @ConfigurationProperties classes.

Spring Boot src/main/resources/application.properties ফাইল থেকে configuration পড়ে (YAML-ও চলে)। যেকোনো key @Value বা typed @ConfigurationProperties দিয়ে ব্যবহার করা যায়।
application.properties
server.port=8080
spring.application.name=abcltech-orders

# Business config
bkash.merchant=ABCL_MERCH_007
bkash.timeout.ms=2500

7. Vocabulary

TermMeaningবাংলায়
BeanAn object managed by the Spring container.Spring-পরিচালিত object।
Application contextThe container that holds all beans.সব bean ধরে রাখা container।
Component scanClasspath scan for annotated classes.Classpath-এ annotation-যুক্ত class খোঁজা।
AutoconfigurationSensible defaults based on what's on the classpath.Classpath দেখে default চালু করা।
ScopeBean lifetime: singleton, prototype, request…Bean-এর জীবনকাল।
ProfileNamed environment (dev, staging, prod).Env-ভিত্তিক configuration।

8. Practice Problems

  1. Extend the hand-rolled DI demo with a Logger that both OrderService and OrderController depend on. Verify the log line prints at each layer.
    হাতে-লেখা DI demo-তে একটি Logger যোগ করুন যা Service ও Controller দুই জায়গায় dependency। প্রতিটি layer-এ log যায় কিনা যাচাই করুন।
    ✨ Show Answer
    Main.java
    class Logger { void log(String m){ System.out.println("[LOG] "+m); } }
    
    class OrderService {
        private final Logger log;
        OrderService(Logger log){ this.log = log; }
        String summary(){ log.log("service"); return "42"; }
    }
    
    class OrderController {
        private final OrderService svc; private final Logger log;
        OrderController(OrderService s, Logger l){ svc=s; log=l; }
        String handle(){ log.log("controller"); return svc.summary(); }
    }
    
    class Main {
        public static void main(String[] a) {
            Logger l = new Logger();
            System.out.println(new OrderController(new OrderService(l), l).handle());
        }
    }
  2. Why is constructor injection preferred over field injection (@Autowired on a field)?
    Constructor injection field injection-এর চেয়ে কেন ভালো?
    ✨ Show Answer

    Answer: (1) Dependencies become final and can never be forgotten — the compiler refuses to build an incomplete object. (2) The class is testable without Spring — you just call its constructor in a test. (3) Circular dependencies surface as clear compile/startup errors instead of mysterious runtime NPEs. (4) The class's required collaborators become visible at a glance from its signature.

  3. Write what a Spring Boot @RestController method for GET /health returning {"status":"UP"} looks like.
    Spring Boot-এ GET /health route লিখুন যা {"status":"UP"} JSON ফেরাবে।
    ✨ Show Answer
    HealthController.java
    import org.springframework.web.bind.annotation.*;
    import java.util.Map;
    
    @RestController
    class HealthController {
        @GetMapping("/health")
        public Map<String,String> health() {
            return Map.of("status", "UP");
        }
    }
  4. Build a hand-rolled "Spring profile" switcher: a Config class reads an env variable APP_ENV (default dev) and prints a different greeting for dev vs prod.
    হাতে-লেখা Spring profile বানান — env variable APP_ENV পড়ে dev/prod-এ ভিন্ন greeting প্রিন্ট করুন।
    ✨ Show Answer
    Main.java
    class Main {
        public static void main(String[] args) {
            String env = System.getenv("APP_ENV");
            if (env == null) env = "dev";
            System.out.println(switch (env) {
                case "prod" -> "Running in PRODUCTION — be careful.";
                case "dev"  -> "DEV mode — debug logs ON.";
                default      -> "Unknown env: " + env;
            });
        }
    }
  5. Explain, in 3 sentences, what happens when you call SpringApplication.run(App.class, args).
    তিন বাক্যে বলুন — SpringApplication.run(App.class, args) চালালে কী ঘটে।
    ✨ Show Answer

    Answer: (1) Spring bootstraps the ApplicationContext and scans the classpath for @Component / @Service / @RestController beans, instantiating and wiring them in dependency order. (2) Auto-configuration kicks in — Spring detects that spring-boot-starter-web is on the classpath and starts the embedded Tomcat server on port 8080. (3) The app stays up, listens for HTTP traffic, and dispatches requests to your mapped handler methods until it is stopped.

Summary — Module 47

Spring Boot is the de-facto Java web framework, and its heart is dependency injection — you declare what you need, the container wires it up. A few annotations (@RestController, @Service, @Repository) replace hundreds of lines of glue code. Always prefer constructor injection; it keeps your code testable and Spring-agnostic.

Spring Boot Java web framework-এর de-facto choice — এর হৃদয় হলো dependency injection। কিছু annotation দিয়েই শত-শত line glue code মুছে যায়। সব সময় constructor injection বেছে নিন — testable থাকে, Spring ছাড়াই চলে।

Next Module → REST APIs with Spring Boot + JPA — একটি সম্পূর্ণ CRUD API।