Before we look at Spring and Spring Boot, we need to understand what “Servlet” and “Servlet Container” is.

  • Provides foundation for building web applications.
  • Servlet is a JAVA class which handles client’s request, processes it and then return the response.
  • Servlet Container manages the Servlet.

Left Servlet Class and it’s mapping happens in web.xml which tends to become tedious if a lot of servlets used to come.

Tomcat is basically Servlet Container, containing multiple servlets.

Spring MVC

Solves challenges which exists in servlets.

  • Removal of Web.xml
    • This file becomes very large over a period of time and difficult to manage.
    • Spring Framework introduced Annotation based configuration.
  • Inversion of Control (a.k.a Dependency Injection : Implementation of IOC to be precise)
    • More flexible way to manage object dependencies and it’s lifecycle.
    • Helps while testing the class.

Payment class is creating an instance of User class, which is a major problem here because of tight coupling of User class with Payment method.

  • Difficult to mange REST APIs
    • Handling diff HTTP methods, request parameters, path mapping make code little difficult to understand. (Web.xml)
    • Spring MVC provides organized approach to handle requests and it’s easy to build RESTful APIs.

Spring MVC architecture

Dispatcher Servlet :Also known as the front controller in Spring MVC. It is the central component that intercepts all HTTP requests coming into the application. The DispatcherServlet delegates requests to the appropriate handlers (controllers), manages the execution flow, and returns the appropriate response. We’ve to deploy our application on Tomcat.

Instead of Servlet Container, DispatcherServlet decides which controller and endpoint to invoke for the request. How DispatcherServlet works?

  1. Uses HandlerMapping((@RequestMapping)) - The DispatcherServlet uses HandlerMapping to decide which controller and method should handle the incoming request, based on the request URL and other criteria.
  2. Now, DispatcherServlet will create an instance of PaymentController class and to create this instance it will leverage IoC to resolve the PaymentDAO(@Autowired) dependency, create the object and inject it.
  3. The DispatcherServlet invokes the appropriate controller method to handle the request. The controller processes the request, interacts with the service layer, and return the response.

To write a simple Spring MVC we need to create all these classes (along with this Dispatcher Servlet class).

In Dispatcher Servlet class we need to tell Spring MVC that which mapping it’ll handle (”/” : means all), which config class (AppConfig.class) to choose, etc.

Advantage of Springboot over Spring MVC
  1. Dependency Management : No need to add different dependencies separately and also take care which version of a particular dependency is compatible with which other version of other dependency. Just mention the springboot version.
  2. Auto Configuration : No need to separately configure “Dispatcher Servlet”, “AppConfig”, “EnableWebMvc”, “Component Scan”. Springboot add internally by-default. @SpringbootApplication. If you want to change the default values you can define these and override the defaults.
  3. Embedded Server : In traditional Spring MVC app, we need to build a WAR file, which is packaged file containing your application’s classes, JSP pages, configuration files and dependencies. Then we need to deploy this WAR file to a servlet container like tomcat. But in Springboot, Servlet container is already embedded, we don’t need to do any of this but to just run the application.

SpringBoot Layered Architecture

Annotations

@RestController vs @Controller

The @Controller annotation is used to define a controller that handles HTTP requests and typically returns a view (like an HTML page). The @RestController annotation is a specialized version of @Controller. It is used to create RESTful web services that return data (usually in JSON or XML format) directly to the client instead of rendering a view.

@RestController Combines @Controller and @ResponseBody: When you annotate a class with @RestController, it is equivalent to annotating the class with both @Controller and @ResponseBody. This means that all the methods in the class will have their return values automatically serialized into the appropriate format (e.g., JSON) and sent directly as the HTTP response body.

  1. View vs. Data:
    • @Controller: Typically used for web applications where the response is an HTML page or another type of view.
    • @RestController: Used for RESTful APIs where the response is data (e.g., JSON, XML) rather than a view.
  2. Automatic Serialization:
    • @Controller: Methods annotated with @Controller return views by default. You need to use @ResponseBody on individual methods if you want to return data directly (like JSON). (see attached image for more clarity)
    • @RestController: Methods automatically serialize their return values into JSON or XML and write them directly to the response body, eliminating the need for @ResponseBody.

@RequestMapping

It maps HTTP requests to handler methods of @Controller classes.

Class-Level vs. Method-Level @RequestMapping

  1. Class-Level @RequestMapping
    • When applied at the class level, @RequestMapping defines a base URL for all request mappings in the controller.
    • All method-level @RequestMapping annotations within the class will be relative to this base URL.
@RestController
@RequestMapping("/api/users")
public class UserController {
 
    @GetMapping("/{id}")
    public ResponseEntity<User> getUserById(@PathVariable Long id) {
        // Handle the request and return a response
    }
 
    @PostMapping("/")
    public ResponseEntity<User> createUser(@RequestBody User user) {
        // Handle the request and return a response
    }
}
  1. Method-Level @RequestMapping**:
    • When applied at the method level, @RequestMapping defines the specific URL pattern and HTTP method that the method handles.
@GetMapping("/users/{id}")
public ResponseEntity<User> getUserById(@PathVariable Long id) {
    // Handle the request and return a response
}
@RequestMapping

@PathVariable

Note

Following REST principles, path parameters should represent the location or unique identity of a resource, while request parameters should be used for operations on that resource, such as filtering or sorting.

Path Parameters: Used when the value is essential to locating or identifying the resource. They are most appropriate for identifying unique entities. Example: /users/123 — Here, 123 is a path parameter that uniquely identifies a specific user.

Request Parameters: Used when the value is optional or modifies the query. They are appropriate for search queries, filters, pagination, sorting. URL: /users?role=admin&active=true Purpose: Fetches all users with the role of admin who are also active.

@RequestBody

curl --location 'http://localhost:8080/api/saveUser' \
--header 'Content-Type: application/json' \
--data-raw '{
    "user_name" : "Shreyansh",
    "email" : "sTomar@gmail.com"
}'
  • Jackson or GSON knows to map “user_name” in request body to map it to User POJO.
@Response Entity

Represents the entire HTTP response. Header, status, response body, etc.

@SpringbootApplication

The @SpringBootApplication annotation combines three annotations. Those three annotations are: @Configuration, @EnableAutoConfiguration, and @ComponentScan.

  • @AutoConfiguration : This annotation automatically configuring beans in the class path and automatically scans the dependencies according to the application need.
  • @ComponentScan : This annotation scans the components (@Component, @Service, etc.) in the package of annotated class and its sub-packages.
  • @Configuration: This annotation configures the beans and packages in the class path.

@SpringBootApplication automatically configures the application based on the dependencies added during project creation and bootstraps the application by using run() method inside the main class of an application.

@SpringBootApplication = @Configuration + @EnableAutoConfiguration + @ComponentScan

@Component

Spring marks a class as a Spring bean. We’re basically telling Spring that you manage this class now.

Stereotype Annotations (Intention Clarity, Maintainability )

More specialized versions of @Component, it’s just that hum spring ko thoda aur idea de rahe ki what this class may contain.

1. @Service:

Indicates that a class is a service, typically holding business logic. It is essentially the same as @Component but has semantic meaning.

2. @Repository:

Indicates that a class is a Data Access Object (DAO) that interacts with a database. It is also a specialization of @Component.

3. @Controller:

Indicates that a class is a Spring MVC controller that handles web requests. It is another specialization of @Component.

@Autowired

Automatically wires up the dependency for you. Enables loose coupling b/w classes and dependencies gets wired up in the background. First look for the bean of the required type. If bean found, Spring will inject it If not, then you’ll create the bean and then inject it.

@Configuration

Contains logic for wiring spring. It also comes with dependency injection, so nothing is stopping you to write the business logic.

@ComponentScan

used with @Configuration to auto-detect beans across packages.

Spring Dependency Injection with examples

Spring Dependency Injection (DI) is a fundamental concept in the Spring Framework that allows objects to be injected into other objects, promoting loose coupling, easier testing, and better manageability of code. Spring DI enables the creation of objects, the wiring of dependencies, and the lifecycle management of objects to be handled by the Spring container.

Key Concepts of Spring DI

  1. Bean: A Java object that is instantiated, assembled, and managed by the Spring IoC container.
  2. Dependency Injection: The process of providing dependencies (objects) to a class from outside rather than the class creating them internally.
  3. IoC Container: The Spring container that manages the lifecycle, configuration, and dependencies of objects (beans).

Types of Dependency Injection in Spring

  1. Constructor Injection: Dependencies are provided through a class constructor.
  2. Setter Injection: Dependencies are provided through setter methods.
  3. Field Injection: Dependencies are directly assigned to fields.

Example 1: Constructor Injection

@Component
public class MessageService {
    private final MessageRepository messageRepository;
 
    @Autowired
    public MessageService(MessageRepository messageRepository) {
        this.messageRepository = messageRepository;
    }
 
    public String getMessage() {
        return messageRepository.fetchMessage();
    }
}
 
@Component
public class MessageRepository {
    public String fetchMessage() {
        return "Hello, Dependency Injection!";
    }
}
 
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {}
 
public class MainApplication {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        MessageService messageService = context.getBean(MessageService.class);
        System.out.println(messageService.getMessage());
    }
}

Example 2: Setter Injection

@Component
public class NotificationService {
    private EmailService emailService;
 
    @Autowired
    public void setEmailService(EmailService emailService) {
        this.emailService = emailService;
    }
 
    public void sendNotification(String message) {
        emailService.sendEmail(message);
    }
}
 
@Component
public class EmailService {
    public void sendEmail(String message) {
        System.out.println("Sending email: " + message);
    }
}
 
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {}
 
public class MainApplication {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        NotificationService notificationService = context.getBean(NotificationService.class);
        notificationService.sendNotification("Dependency Injection Example");
    }
}

Example 3: Field Injection

@Component
public class OrderService {
    @Autowired
    private PaymentService paymentService;
 
    public void processOrder() {
        paymentService.processPayment();
    }
}
 
@Component
public class PaymentService {
    public void processPayment() {
        System.out.println("Processing payment...");
    }
}
 
@Configuration
@ComponentScan(basePackages = "com.example")
public class AppConfig {}
 
public class MainApplication {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        OrderService orderService = context.getBean(OrderService.class);
        orderService.processOrder();
    }
}