02 Spring Boot Spring Core
02 Spring Boot Spring Core
© luv2code LLC
Inversion of Control (IoC)
My App CricketCoach
configuration
CricketCoach BaseballCoach
HockeyCoach
Object
give me a “Coach” object
Factory
My App
configuration
CricketCoach BaseballCoach
HockeyCoach
© luv2code LLC
Dependency Injection
configuration
dependencies
(helper objects) CricketCoach HockeyCoach BaseballCoach
dependencies dependencies
(helper objects) (helper objects)
www.luv2code.com © luv2code LLC
Spring Container
Spring
• Primary functions Object
Factory
• Create and manage objects (Inversion of Control)
• This is a dependency
Coach
• Constructor Injection
• Setter Injection
• Setter Injection
• If dependency is not provided, your app can provide reasonable default logic
Coach
Coach
/dailyworkout getDailyWorkout()
package com.luv2code.springcoredemo;
import org.springframework.stereotype.Component;
@Component
public class CricketCoach implements Coach {
package com.luv2code.springcoredemo;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@RestController
public class DemoController { @Autowired annotation tells
Spring to inject a dependency
private Coach myCoach;
@Autowired
public DemoController(Coach theCoach) {
myCoach = theCoach;
}
}
At the moment, we only have one Coach implementation CricketCoach.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class DemoController {
@Autowired
public DemoController(Coach theCoach) {
myCoach = theCoach;
}
@GetMapping("/dailyworkout")
public String getDailyWorkout() {
return myCoach.getDailyWorkout();
}
}
© luv2code LLC
How Spring Processes your application
File: Coach.java File: CricketCoach.java File: DemoController.java
package com.luv2code.springcoredemo; package com.luv2code.springcoredemo; package com.luv2code.springcoredemo;
… …
public interface Coach {
@Component @RestController
String getDailyWorkout(); public class CricketCoach implements Coach { public class DemoController {
}
@Override private Coach myCoach;
public String getDailyWorkout() {
return "Practice fast bowling for 15 minutes"; @Autowired
} public DemoController(Coach theCoach) {
} myCoach = theCoach;
}
…
}
Spring Framework
• For small basic apps, it may be hard to see the benefits of Spring
© luv2code LLC
Scanning for Component Classes
• Spring will scan your Java classes for special annotations
• @Component, etc …
package com.luv2code.springcoredemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringcoredemoApplication {
package com.luv2code.springcoredemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringcoredemoApplication { Enables
public static void main(String[] args) {
Auto configuration
SpringApplication.run(SpringcoredemoApplication, args);
} Component scanning
} Additional configuration
package com.luv2code.springcoredemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
Composed
public class SpringcoredemoApplication { of following annotations
public static void main(String[] args) {
@EnableAutoConfiguration
SpringApplication.run(SpringcoredemoApplication, args);
} @ComponentScan
} @Configuration
Annotation Description
package com.luv2code.springcoredemo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringcoredemoApplication {
@SpringBootApplication
Starts the embedded server
public class SpringcoredemoApplication { Tomcat etc…
public static void main(String[] args) {
SpringApplication.run(SpringcoredemoApplication, args);
}
}
• com.luv2code.springcoredemo
Explicitly list
base packages to scan
• But what about my other packages?
• com.luv2code.util
org.acme.cart package com.luv2code.springcoredemo;
•
…
edu.cmu.srs @SpringBootApplication(
•
scanBasePackages={"com.luv2code.springcoredemo",
"com.luv2code.util",
"org.acme.cart",
"edu.cmu.srs"})
public class SpringcoredemoApplication {
…
}
© luv2code LLC
Spring Injection Types
• Constructor Injection
• Setter Injection
Coach
Step-
By-S
tep
1. Create setter method(s) in your class for injections
@RestController
public class DemoController {
…
}
@RestController
public class DemoController {
@Autowired
public void setCoach(Coach theCoach) {
myCoach = theCoach;
}
…
}
Spring Framework
Setter injection
Coach theCoach = new CricketCoach();
demoController.setCoach(theCoach);
@RestController
public class DemoController {
…
}
• Setter Injection
• If dependency is not provided, your app can provide reasonable default logic
© luv2code LLC
Spring Injection Types
• Recommended by the spring.io development team
• Constructor Injection: required dependencies
• Setter Injection: optional dependencies
import org.springframework.beans.factory.annotation.Autowired;
…
@RestController
public class DemoController {
@Autowired
private Coach myCoach; Field injection
// no need for constructors or setters
@GetMapping("/dailyworkout")
public String getDailyWorkout() {
return myCoach.getDailyWorkout(); Field injection is not recommended by
}
} spring.io development team.
© luv2code LLC
Autowiring DemoController
Coach
Coach
CricketCoach TennisCoach
BaseballCoach
TrackCoach
@Component @Component
public class CricketCoach implements Coach { public class BaseballCoach implements Coach {
@Override @Override
public String getDailyWorkout() { public String getDailyWorkout() {
return "Practice fast bowling for 15 minutes"; return "Spend 30 minutes in batting practice";
} }
} }
package com.luv2code.springcoredemo.common;
package com.luv2code.springcoredemo.common;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Component;
@Component
@Component
public class TrackCoach implements Coach {
public class TennisCoach implements Coach {
@Override
@Override
public String getDailyWorkout() {
public String getDailyWorkout() {
return "Run a hard 5k!";
return "Practice your backhand volley";
}
}
}
}
- baseballCoach
- cricketCoach
- tennisCoach
- trackCoach
import com.luv2code.springcoredemo.common.Coach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
… Specify the bean id: cricketCoach
@RestController
public class DemoController { Same name as class, first character lower-case
private Coach myCoach;
@Autowired
public DemoController(@Qualifier("cricketCoach") Coach theCoach) {
myCoach = theCoach;
}
@GetMapping("/dailyworkout")
public String getDailyWorkout() { Other bean ids we could use:
return myCoach.getDailyWorkout();
}
baseballCoach, trackCoach, tennisCoach
}
import com.luv2code.springcoredemo.common.Coach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
… Give bean id: cricketCoach
@RestController
public class DemoController { Same name as class, first character lower-case
private Coach myCoach;
@Autowired
public void setCoach(@Qualifier("cricketCoach") Coach theCoach) {
myCoach = theCoach;
}
@GetMapping("/dailyworkout")
public String getDailyWorkout() { Other bean ids we could use:
return myCoach.getDailyWorkout();
}
baseballCoach, trackCoach, tennisCoach
}
© luv2code LLC
Resolving issue with Multiple Coach implementations
• Then you coaches figure it out … and tell me who’s the primary coach
@Component
@Primary
public class TrackCoach implements Coach {
@Component
public class TennisCoach implements Coach {
@Override …
public String getDailyWorkout() { }
return "Run a hard 5k!";
}
}
@Component
public class CricketCoach implements Coach {
…
}
import com.luv2code.springcoredemo.common.Coach;
import org.springframework.beans.factory.annotation.Autowired; No need for @Qualifier
…
@Autowired
public DemoController(Coach theCoach) {
myCoach = theCoach; package com.luv2code.springcoredemo.common;
} import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@GetMapping("/dailyworkout")
@Component
public String getDailyWorkout() { @Primary
return myCoach.getDailyWorkout(); public class TrackCoach implements Coach {
}
@Override
} public String getDailyWorkout() {
return "Run a hard 5k!";
}
}
• When using @Primary, can have only one for multiple implementations
import com.luv2code.springcoredemo.common.Coach;
import org.springframework.beans.factory.annotation.Autowired; @Qualifier has higher priority
import org.springframework.beans.factory.annotation.Qualifier;
…
Even though there is a @Primary coach
@RestController This example will use CricketCoach
public class DemoController {
@Autowired
public DemoController(@Qualifier("cricketCoach") Coach theCoach) { package com.luv2code.springcoredemo.common;
myCoach = theCoach; import org.springframework.context.annotation.Primary;
} import org.springframework.stereotype.Component;
@Component
@GetMapping("/dailyworkout") @Primary
public String getDailyWorkout() { public class TrackCoach implements Coach {
return myCoach.getDailyWorkout();
@Override
} public String getDailyWorkout() {
} return "Run a hard 5k!";
}
}
• More specific
• Higher priority
© luv2code LLC
Initialization
• @Component, etc …
@Component @Component
public class TrackCoach implements Coach { public class TennisCoach implements Coach {
…
In constructor: BaseballCoach
In constructor: CricketCoach
In constructor: TennisCoach
In constructor: TrackCoach
…
• Or it is explicitly requested
@Autowired …
public DemoController(@Qualifier("cricketCoach") Coach theCoach) { In constructor: BaseballCoach
myCoach = theCoach;
}
In constructor: CricketCoach
… In constructor: TennisCoach
} …
File: application.properties
@Autowired …
public DemoController(@Qualifier("cricketCoach") Coach theCoach) {
System.out.println("In constructor: " + getClass().getSimpleName()); In constructor: CricketCoach
myCoach = theCoach;
} In constructor: DemoController
…
} …
• May help with faster startup time if you have large number of components
• Disadvantages
• If you have web related components like @RestController, not created until requested
• Need to make sure you have enough memory for all beans once created
© luv2code LLC
Bean Scopes
• Scope refers to the lifecycle of a bean
• It is cached in memory
@Autowired
public DemoController( CricketCoach
@Qualifier("cricketCoach") Coach theCoach,
@Qualifier("cricketCoach") Coach theAnotherCoach) {
myCoach = theCoach;
anotherCoach = theAnotherCoach;
}
…
}
@Component
@Scope(ConfigurableBeanFactory.SCOPE_SINGLETON)
public class CricketCoach implements Coach {
Scope Description
request Scoped to an HTTP web request. Only used for web apps.
session Scoped to an HTTP web session. Only used for web apps.
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class CricketCoach implements Coach {
@RestController CricketCoach
public class DemoController {
@Autowired CricketCoach
public DemoController(
@Qualifier("cricketCoach") Coach theCoach,
@Qualifier("cricketCoach") Coach theAnotherCoach) {
myCoach = theCoach;
anotherCoach = theAnotherCoach;
}
…
}
@GetMapping("/check")
public String check() {
return "Comparing beans: myCoach == anotherCoach, " + (myCoach == anotherCoach);
}
…
}
© luv2code LLC
Bean Lifecycle
Container Is Shutdown
Your Custom
Destroy Method
public CricketCoach() {
System.out.println("In constructor: " + getClass().getSimpleName());
}
@PostConstruct
public void doMyStartupStuff() {
System.out.println("In doMyStartupStuff(): " + getClass().getSimpleName());
}
…
}
public CricketCoach() {
System.out.println("In constructor: " + getClass().getSimpleName());
}
@PostConstruct
public void doMyStartupStuff() {
System.out.println("In doMyStartupStuff(): " + getClass().getSimpleName());
}
@PreDestroy
public void doMyCleanupStuff() {
System.out.println("In doMyCleanupStuff(): " + getClass().getSimpleName());
}
…
}
Step-
By-S
tep
1. Define your methods for init and destroy
© luv2code LLC
Our New Coach …
No sp
anno e cial
tation
s
package com.luv2code.springcoredemo.common;
}
Coach
package com.luv2code.springcoredemo.config;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SportConfig {
…
}
import com.luv2code.springcoredemo.common.Coach;
import com.luv2code.springcoredemo.common.SwimCoach;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SportConfig {
import com.luv2code.springcoredemo.common.Coach;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@Autowired
public DemoController(@Qualifier("swimCoach") Coach theCoach) {
System.out.println("In constructor: " + getClass().getSimpleName());
myCoach = theCoach;
}
…
}
• You may not have access to the source code of third-party class
• However, you would like to use the third-party class as a Spring bean
…
import software.amazon.awssdk.auth.credentials.ProfileCredentialsProvider;
import software.amazon.awssdk.regions.Region;
import software.amazon.awssdk.services.s3.S3Client;
@Configuration
public class DocumentsConfig {
@Bean
public S3Client remoteClient() {
return s3Client;
}
}
import software.amazon.awssdk.services.s3.S3Client;
…
@Component
public class DocumentsService {
@Autowired
public DocumentsService(S3Client theS3Client) {
s3Client = theS3Client;
}
…
}
import software.amazon.awssdk.services.s3.S3Client;
…
@Component
public class DocumentsService {
@Autowired
public DocumentsService(S3Client theS3Client) {
s3Client = theS3Client;
}
// perform the putObject operation to AWS S3 ... using our autowired bean
s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(fileInputStream, contentLength));
}
}
• The Amazon S3 Client class was not originally annotated with @Component
• It is now a Spring Bean and we can inject it into other services of our application