Post

Udemy Spring boot course: Section 2 Spring Core

Inversion of Control (IoC)

  • the approach of outsourcing the construction and management of objects

Coding Scenario

  • alt-text

Spring Solution

  • The app requests a coach object from spring
  • Spring will then use the Object Factory to return the reference to the correct object
  • Spring determines which object you need based on the configuration you set
  • alt-text

Dependency Injection

  • Dependency Inversion Principle - client delegates to another object the responsiblity of providing its dependencies

Car Factory Example

  • user says give me a car object
    • car factory assemblies the car and all its parts and components
    • once the car factory is finished it will return the user the car object
  • alt-text

Team example

  • The application requests a coach object
  • Spring object factory assemblies the correct object based on the configuration and returns the reference to the user
  • alt-text

Types of Injection

  • There are two types of injection
    • Constructor injection
    • Setter injection

Constructor Injection

  • use this when you have required dependencies
  • recommended as first choice

Constructor injection example

  • alt-text
    • In this example we want the spring container to autowire a coach bean to our DemoController class
1
2
3
public interface Coach{
  String getDailyWorkout();
}
1
2
3
4
5
6
7
@Component
public class CricketCoach implements Coach{
  @Override
  public String getDailyWorkout() {
    return "Practice fast bowling for 15 minutes;
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@RestController
public class DemoController {
  private Coach mycoach;

  @Autowired
  public DemoController(Coach thecoach){
    this.mycoach = thecoach;
  }

  @GetMapping("/getworkout")
  public String getDailyWorkout(){
    return mycoach.getDailyWorkout();
  }
}
  • @Component annotation marks the class as a Spring Bean
    • A spring bean is just a regular Java class that is managed by Spring
    • It also makes the class available for Dependency Injection
  • @RestController annoation tells spring this is a class that lists rest routes
  • @AutoWired annoation tells spring to infer the type and spring bean to be injected into the constructor when calling this restcontroller at creation
    • if you only have 1 constructor then @Autowired on constructor is optional

Setter Injection

  • use this when you have optional dependencies
  • if dependency is not provided, your app can provide reasonable default logic

Setter Injection example

  • inject dependencies by calling setter methods on your POJO class
  • can have any setter method name, Spring will infer based on arguments and property being set on what bean to inject
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@RestController
public class Controller {
    private Coach mycoach;

    // SETTER INJECTION
    @Autowired
    public void setMycoach(Coach mycoach) {
        this.mycoach = mycoach;
    }

    @GetMapping("/")
    public String test(){
        return "Hello world";
    }

    @GetMapping("/getdailyworkout")
    public String getdailyWorkout(){
        return this.mycoach.getDailyWorkout();
    }
}

Field Injection

  • There is a 3rd injection type that is not recommended
  • not popular like it used to be in the old days
  • reason why is because it makes it harder to unit test
  • inject beans into class properties even private ones
  • behind the scenes accomplishes this with java reflection.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@RestController
public class Controller {
    @Autowired
    private Coach mycoach;

    // no need for constructor or setters

    @GetMapping("/")
    public String test(){
        return "Home route";
    }

    @GetMapping("/getdailyworkout")
    public String getdailyWorkout(){
        return this.mycoach.getDailyWorkout();
    }
}

Spring Autowiring

  • simplifies the process of injecting dependencies into Spring beans (components or objects).
  • Dependency injection is a fundamental concept in Spring, and autowiring is one way to achieve it.
  • Autowiring allows you to automatically inject the required dependencies into a Spring bean without explicitly specifying them in the configuration.
  • Spring can automatically identify and inject the appropriate dependencies based on certain rules and annotations.

Types of Autowiring

TypeDescription
No Autowiring (Default)must explicitly specify the dependencies using elements in XML configuration or @Autowired annotations in Java-based configuration.
Autowire by Type (autowire=”byType”)Spring automatically wires dependencies by matching their types. If there are multiple beans of the same type, Spring will raise an exception
Autowire by Name (autowire=”byName”)Spring automatically wires dependencies by matching their
Autowire by Constructor (autowire=”constructor”):Spring automatically wires dependencies by examining the constructor of the bean.
Autowire by Qualifier (@Qualifier annotation):When you have multiple beans of the same type, you can use the @Qualifier annotation along with @Autowired to specify which bean should be injected.

Spring Container

Primary Functions

  • Create and manage objects (Inversion of control)
  • Injeft object dependencies (Dependency Injection)

Configuration of the spring container

Configuration TypeRelevance
XML ConfigurationLegacy
Java AnnotationsModern
Java Source CodeModern

@SpringBootApplication annotation

  • @SpringBootApplication enables the following by default
    • @EnableAutoConfiguration
    • @ComponentScan
    • @Configuration
1
2
3
4
5
6
7
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DependencyInjectionDemoApplication {
  SpringApplication.run(DependencyInjectionDemoApplication.class, args);
}
AnnotationDescription
@EnableAutoConfigurationEnables Springboots auto-configuration support
@ComponentScanEnables component scanning of current package. Also recursively scans sub-packages
@ConfigurationAble to register extra beans with @Bean or import other configuration classes
  • SpringApplication.run()
    • Creates application context and registers all beans
    • Starts the embedded server Tomcat, etc..

Component Scanning

  • alt-text
  • default, spring scans all sub packages from where the main method file is located
    • scans these packages recursively
  • doesn’t matter the name of the packages

Specify explicitly

  • we can specify explicitly what packages to scan with scanBasePackages= {...}
1
2
3
4
5
6
7
8
9
10
@SpringBootApplication(
		scanBasePackages= {"springboot.something","springboot.other"}
)
public class DependencyInjectionDemoApplication {
	public static void main(String[alt-text] args) {
		SpringApplication.run(DependencyInjectionDemoApplication.class, args);
	}

}

@Qualifer

  • when you have multiple beans and need to specify one

By default the component name for a class will camelCase with the first letter being lowercase. In order to override this you have to specify with @Component(“BeanName”)

1
2
3
4
5
6
7
8
@Component("FootballCoach")
public class FootballCoach implements Coach{

    @Override
    public String getDailyWorkout() {
        return "10 up-downs, and hit  25 people";
    }
}

Then to specify which bean you want injected you can use the @Qualifer(“BeanName”)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@RestController
public class Controller {
    private Coach mycoach;

    @Autowired
    public Controller(@Qualifier("footballCoach") Coach mycoach) {
        this.mycoach = mycoach;
    }

    @GetMapping("/")
    public String test(){
        return "Home route";
    }

    @GetMapping("/getdailyworkout")
    public String getdailyWorkout(){
        return this.mycoach.getDailyWorkout();
    }
}

@Primary

  • You can use the @Primary annotation instead or instead or in conjunction with the @Qualifer annotation
  • It goes in the @Component class
  • That way if you have multiple coach types it will inject the class with the @Primary annotation as default
  • You will get an error if more than one bean has the @Primary annotation
  • If you have a @Primary bean and specify a bean in @Qualifer, the @Qualifer has higher priority
1
2
3
4
5
@Component
@Primary
public class BaseballCoach implements Coach{
  // implementation
}

Lazy Initialization @Lazy

  • Lazy initialization is the pattern of putting off the creation of an object or process until it is needed.
  • By default, when your app starts, all beans are initialized. Regardless if they are going to be used or not
  • We can setup lazy initialization where the beans is only created if:
    • It is needed for dependency injection
    • it is explictly requested
1
2
3
4
5
6
7
8
@Component
@Lazy
public class BaseballCoach implements Coach{
    @Override
    public String getDailyWorkout() {
        return "Do 10 poles and hit 5 homeruns";
    }
}
AdvantagesDisadvantages
Only create objects as neededWeb related components have to create the beans needed on the first request
Helps with faster startup timemay not see configuration issues until too late

Global Configuration

  • Using the @Lazy for every single component could get tedious
  • We could use the global configuration to make all components to load lazyily
1
spring.main.lazy-initialization=true

Bean Scope

  • Default scope is Singleton
    • spring container only creates 1 instance of a bean
    • it is cached in memory
    • all dependency injections for a bean will reference the same bean
  • alt-text

Specify Bean Scope @Scope

ScopeDescription
SingletonScopes a single bean definition to a single object instance per Spring IoC container.
PrototypeScopes a single bean definition to any number of object instances.
SessionScopes a single bean definition to the lifecycle of a HTTP Session. Only valid in the context of a web-aware Spring ApplicationContext.
Global SessionScopes a single bean definition to the lifecycle of a global HTTP Session. Typically only valid when used in a portlet context. Only valid in the context of a web-aware Spring ApplicationContext.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class FootballCoach implements Coach{

  public class FootBallCoach(){
    System.out.println("FootballCoach constructor()");
  }
    
    @Override
    public String getDailyWorkout() {
        return "10 up-downs, and hit  25 people";
    }
    
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
public class Controller {
    private Coach mycoach;
    private Coach otherCoach;

    @Autowired
    public Controller(@Qualifier("footballCoach") Coach mycoach,
                      @Qualifier("footballCoach") Coach otherCoach) {
        this.mycoach = mycoach;
        this.otherCoach = otherCoach;
    }

    @GetMapping("/check")
    public String check(){
        return "Comparing beans: myCoach == otherCoach: " + ((mycoach ==  otherCoach) ? "True": "False");
    }
  • if we went to /other
    • we wold see “False”, because myCoach and otherCoach are two different objects since using the prototype scope

Singleton

  • One bean instance per spring container

Prototype

  • Scopes single bean to any number of object instances
  • alt-text

Bean LifeCycle

  • alt-text
  • if we want to execute some code on the bean instantiation and just after closing the spring container, then we can write that code inside the custom init() method and the destroy() method.
  • What is the point of the custom init/destroy methods
    • custom code during bean initialization, destroy

@PostConstruct init()

  • The @PostCostruct is the init() method
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Component
public class FootballCoach implements Coach{

    public FootballCoach() {
        System.out.println("IN CONSTRUCTOR: " + getClass().getSimpleName());
    }

    // init method
    @PostConstruct
    public void doStartupStuff(){
        System.out.println("football coach needs redbull to get started...");
    }

    @Override
    public String getDailyWorkout() {
        return "10 up-downs, and hit  25 people";
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@RestController
public class Controller {
    private Coach mycoach;

    @Autowired
    public Controller(@Qualifer("footballCoach")Coach mycoach) {
        this.mycoach = mycoach;
    }


    @GetMapping("/getdailyworkout")
    public String getdailyWorkout(){
        return this.mycoach.getDailyWorkout();
    }

}
  • Output will be the following
1
2
IN CONSTRUCTOR: FootballCoach
football coach needs redbull to get started...

@PreDestroy destroy() method

  • The @PreDestroy is the destroy() method
  • The destroy() method is not called on beans with Prototype scope
1
2
3
4
@PreDestroy
public void doCleanupStuff(){
    System.out.println("Cleaning up the rest of the stuff");
}

Configure Beans with java code @Configuration & @Bean

  • you can configure beans with java code instead of annotations
  • When does it make sense to use a configuration file instead of @Component on classes
    • make an existing 3rd party class available to the Spring Framework
  • Steps
    • Need to configure class with @Configuration annotation
    • Configure @Bean method that returns a bean
  • alt-text
1
2
3
4
5
6
public class SwimCoach implements Coach{
    @Override
    public String getDailyWorkout() {
        return "swim 10 laps";
    }
}
1
2
3
4
5
6
7
@Configuration
public class SportsConfig {
  @Bean("swimmingCoach") // overriding the default bean id which would just be the method name below
  public Coach swimCoach(){ // bean id will default to the method name
      return new SwimCoach();
  }
}
1
2
3
4
@Autowired
public Controller(@Qualifier("swimmingCoach") Coach mycoach) {
    this.mycoach = mycoach;
}
This post is licensed under CC BY 4.0 by the author.