Introduction to spring boot

ยท

9 min read

Before we talk about Spring or Spring Boot, first we need to understand about "SERVLET" and "Servlet Container"

  • Provide foundation for building web applications.

  • Servlet is a Java Class, which handles client request, process it and return the response.

  • And Servlet Container are the ones which manages the Servlets.

above we can see serverlet mapping specifying what all URLS serverlet 1 will handle

Spring Framework (MVC), solve challenges which exists with Servlets.

  • Removal of web.xml

    • this web.ml over the time becomes too big and becomes very difficult to manage and understand.

    • Spring framework introduced Annotations based configuration.

  • Inversion of Control (loC)

    • Servlets depends on Servlet container to create object and maintain its lifecycle.

    • loC is more flexible way to manage object dependencies and its lifecycle (though Dependency injection)

  • Makes Unit Testing much easier (due to loose coupling)

  • Solves Difficult to manage REST APIs

    • Handling different HTTP methods, request parameters, path mapping make code little difficult to understand.

    • Spring MVC provides an organised approach to handle the requests and its easy to build RESTful APls.

The another important feature of Spring framework is lot of INTEGRATION available with other frameworks.

This allow Developers to choose different combination of technologies and framework which best fits their requirements like:

  • Integration with Unit testing framework like Junit or Mockito.

  • Integration with Data Access framework like Hibernate, JDBC, JPA etc.

  • Integration with Asynchronous programming.

  • Similar way, it has different integration available for:

    • Caching

    • Messaging

    • Security etc.

SIMPLE SPRING MVC APPLICATION (NOT SPRING BOOT)

Spring Boot, solve challenges which exists with Spring MVC.

  • Dependency Management: No need for adding different dependencies separately and also the compatible version headache.WE DONT ADD ANY VERSIONS in pom.xml

  • Auto Configuration: No need for separately configuring "DispatcherServlet", "AppConfig","EnableWebMvc", "ComponentScan". Spring boot add internally by-default. The component scan will start from main method. @SpringbootApplication is a composite config annotation which handles all configurations

  • **Embedded Server:**In traditional Spring MVC application, we need to build a WAR file, which is a 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 Spring boot, Servlet container is already embedded, we don't have to do all this stuff.

So, what is Spring boot?

  • It provides a quick way to create a production ready application.

  • It is based on Spring framework.

  • It support "Convention over Configuration".

  • Use default values for configuration, and if developer don't want to go with convention(the way something is done), they can override it.

  • It also help to run an application as quick as possible.

Spring boot layered architecture

  • DTO pacakge specifies the format of Data Transfer Objects (Request DTO/ Response DTO)

  • Utility pacakge container helper classes / functions which are common among classes

  • Entity pacakage contain POJOS which map to DB tables (ORM)

  • Configuration pacakge contains application.properties

Maven

It's a project management tool. Helps developers with:

  • Build generation

  • Dependency resolution

  • Documentation etc.

Maven uses POM (Project Object Model) to achieve this.

  • When "maven" command is given, it looks for "pom.ml" in the current directory & get needed configuration.

MAVEN BUILD LIFECYCLE

  • The <build/> section im pom.xml corresponds with build phases and tasks - phases/tasks can be added or removed

  • Maven already has Validate phase defined and its goal, but if we want to add more goals or task, then we can use <build> element. And add the goal to specific phase.

  • mvn compile: It will validate and compile your code and put it under ${project.basedir}/target/classes

Bean Lifecycle

Dependency Injection

Different ways of Injection and which one is better?

  • Field Injection

    • above @Lazy means that bean created only when used like its being done with @Autowired.(In above screenshot first the constructor object gets printed then Order constructor)

    • DISADVANTAGE:

      • Cannot make filed injected fields immutable

      • How to test this ?

      • Chances of null pointer exceptions

        The above throws a NullPointerException because the Order object is not properly initialized when we manually create an instance of the User class using the new keyword. In Spring, dependency injection is managed by the Spring container, and we you bypass the container by manually instantiating the class, the dependencies are not injected.

        1.   User userObj = new User();
            userObj.process();
          

          When we manually create an instance of User using new User(), Spring does not get a chance to inject the Order dependency.

Below is the corrected code with proper dependency injection:

  1. Order Class:

     @Component
     public class Order {
         public void process() {
             System.out.println("Order processed");
         }
     }
    
  2. User Class:

     @Component
     public class User {
         private final Order order;
    
         @Autowired
         public User(Order order) {
             this.order = order;
             System.out.println("User initialized");
         }
    
         public void process() {
             order.process();
         }
     }
    
  3. Main Application Class:

     @SpringBootApplication
     public class Application {
         public static void main(String[] args) {
             ApplicationContext context = SpringApplication.run(Application.class, args);
             User userObj = context.getBean(User.class);
             userObj.process();
         }
     }
    

Explanation

  1. Order Class:

    • The Order class is annotated with @Component, making it a Spring-managed bean.
  2. User Class:

    • The User class is also annotated with @Component.

    • The Order dependency is injected via the constructor, which is annotated with @Autowired. This ensures that Spring injects the Order bean when it creates an instance of User.(Avoiding field injection)

  3. Main Application Class:

    • The Application class is annotated with @SpringBootApplication, which is a convenience annotation that adds @Configuration, @EnableAutoConfiguration, and @ComponentScan.

    • The ApplicationContext is obtained by running SpringApplication.run(Application.class, args).

    • The User bean is retrieved from the Spring context using context.getBean(User.class), ensuring that all dependencies are properly injected by Spring.

  • Setter Injection

    • Advantage:

      • Dependency can be changed any time after the object creation (as object can not be marked as final).

      • Ease of testing, as we can pass mock object in the dependency easily.

Disadvantage:

  • Field Can not be marked as final. (We can not make it immutable).

  • Difficult to read and maintained, as per standard, object should be initialized during object creation, so this might create code readability issue.

Constructor Injection (Most preferred)

In Spring Boot, dependency injection can be achieved in several ways, including using the @Autowired annotation and constructor injection. Constructor injection is a preferred method for dependency injection in Spring Boot because it promotes immutability and makes your code easier to test.

  • Dependency get resolved at the time of initialization of the Object itself.

  • Its recommended to use

  • When only 1 constructor is present, then using @Autowired on constructor is not mandatory.(from Spring version 4.3)

Why Constructor Injection?

  • Immutability: By using constructor injection, you can make the fields final, which ensures that they are not modified after the object is created.

  • Testing: Constructor injection makes it easier to write unit tests. You can create instances of the class and pass mock dependencies directly to the constructor.

  • Simplicity: It removes the need for the @Autowired annotation, making the code cleaner and more straightforward.

  • Fail Fast: If there is any missing dependency, it will fail during compilation itself, rather than tailing during run Time.

Common Issues with DI

Circular Dependecy

  • First and foremost, can we refactor the code and remove this cycle dependency:

For example, common code in which both are dependent, can be taken out to separate class. This way we can break the circular dependency.

Spring will create proxy bean instead of creating the bean instance immediately during application startup.

Unsatisfied Dependecy

An "unsatisfied dependency" problem in Spring occurs when the Spring container cannot find a suitable bean to inject into a class. This typically results in exceptions such as NoSuchBeanDefinitionException or UnsatisfiedDependencyException. Understanding the causes and solutions for this issue is crucial for developing robust Spring applications.

Common Causes of Unsatisfied Dependency Problems

  1. Missing Bean Definition:

    • The required bean is not defined in the Spring context.
  2. Incorrect Bean Type:

    • The bean type does not match the expected type in the dependent class.
  3. Ambiguous Dependencies:

    • Multiple beans of the same type are available, and Spring cannot decide which one to inject.
  4. Circular Dependencies:

    • Two or more beans depend on each other, causing a circular reference.

Ways to Mitigate Unsatisfied Dependency Problems

1. Ensure Bean Definitions

  • Component Scanning: Ensure that your classes are annotated with appropriate Spring annotations like @Component, @Service, @Repository, or @Controller, and that they are within the packages scanned by Spring.

  • Configuration Classes: Define beans explicitly in configuration classes using @Configuration and @Bean.

      @Configuration
      public class AppConfig {
          @Bean
          public MyService myService() {
              return new MyService();
          }
      }
    

2. Match Bean Types

  • Check Bean Types: Ensure that the type of the bean being injected matches the type expected by the dependent class.

3. Resolve Ambiguous Dependencies

  • @Primary Annotation: Use @Primary to mark one bean as the primary candidate when multiple beans of the same type exist.

  • @Qualifier Annotation: Use @Qualifier to specify which bean to inject when multiple beans of the same type exist.

Dynamically Initialized Beans | Value Annotation

4. Avoid Circular Dependencies

  • Refactor Code: Refactor your code to break circular dependencies. This might involve redesigning your classes or using setter injection instead of constructor injection.

      Java@Component
      public class A {
          private B b;
    
          @Autowired
          public void setB(B b) {
              this.b = b;
          }
      }
    
      @Component
      public class B {
          private A a;
    
          @Autowired
          public void setA(A a) {
              this.a = a;
          }
      }
    
  • Use @Lazy: Use @Lazy to delay the initialization of a bean until it is needed, which can help resolve circular dependencies.

      @Component
      public class A {
          private final B b;
    
          @Autowired
          public A(@Lazy B b) {
              this.b = b;
          }
      }
    
      @Component
      public class B {
          private final A a;
    
          @Autowired
          public B(@Lazy A a) {
              this.a = a;
          }
      }
    

Annotations

  • @Component: tells Spring that, you have to manage this class or bean.

  • @Autowired: tells Spring to resolve and add this object dependency.Look for a bean of thr required type and if found INJECT IT

  • @Controller : Indicates that class in responsible for handling incoming HTTP Requests

  • @RestController: Nothing but controller plus responseBody annotation

  • @RequestMapping: what all apis is this controller hosting

  • @<Verb>Mapping ex. @GetMapping: nothing but request mapping with http method

  • @RequestParam: Used to bind, request parameter to controller method parameter.The framework automatically performs type conversion from the request parameter's string representation to

    the specified type.

  • @PathVariable: Used to extract values from the path of the URL and help to bind it to controller method parameter.

  • @RequestBody: Bind the body of HTTP request (typically JSON) to controller method parameter (java object).

  • ResponseEntity: It represents the entire HTTP response.

    Header, status, response body etc.

How to do type conversion of request paramer string into Custom Object types?:

PROPERTY EDITOR

ย