02-spring-boot-spring-core.pdf
Document Details
Uploaded by RighteousKelpie
University of Ruhuna
Full Transcript
Inversion of Control © luv2code LLC Inversion of Control (IoC) The approach of outsourcing the construction and management of objects. www.luv2code.com © luv2code LLC Coding Scenario getDailyWorkout()...
Inversion of Control © luv2code LLC Inversion of Control (IoC) The approach of outsourcing the construction and management of objects. www.luv2code.com © luv2code LLC Coding Scenario getDailyWorkout() My App CricketCoach App should be configurable Easily change the coach for another sport Baseball, Hockey, Tennis, Gymnastics etc … www.luv2code.com © luv2code LLC Ideal Solution Object give me a “Coach” object Factory My App configuration CricketCoach BaseballCoach HockeyCoach www.luv2code.com © luv2code LLC Spring Container Spring Object give me a “Coach” object Factory My App configuration CricketCoach BaseballCoach HockeyCoach www.luv2code.com © luv2code LLC Spring Container Spring Primary functions Object Factory Create and manage objects (Inversion of Control) Inject object dependencies (Dependency Injection) www.luv2code.com © luv2code LLC Configuring Spring Container XML configuration file (legacy) Java Annotations (modern) Java Source Code (modern) www.luv2code.com © luv2code LLC Spring Dependency Injection © luv2code LLC Dependency Injection The dependency inversion principle. The client delegates to another object the responsibility of providing its dependencies. www.luv2code.com © luv2code LLC Car Factory give me a “Car” object Car Factory www.luv2code.com © luv2code LLC Spring Container Spring Object give me a “Coach” object Factory My App 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) Inject object’s dependencies (Dependency Injection) www.luv2code.com © luv2code LLC Demo Example Coach will provide daily workouts The DemoController wants to use a Coach New helper: Coach DemoController This is a dependency Coach Need to inject this dependency www.luv2code.com © luv2code LLC Injection Types There are multiple types of injection with Spring We will cover the two recommended types of injection Constructor Injection Setter Injection www.luv2code.com © luv2code LLC Injection Types - Which one to use? Constructor Injection Use this when you have required dependencies Generally recommended by the spring.io development team as first choice Setter Injection Use this when you have optional dependencies If dependency is not provided, your app can provide reasonable default logic www.luv2code.com © luv2code LLC What is Spring AutoWiring DemoController Coach For dependency injection, Spring can use autowiring Spring will look for a class that matches matches by type: class or interface Spring will inject it automatically … hence it is autowired www.luv2code.com © luv2code LLC Autowiring Example DemoController Coach Injecting a Coach implementation Spring will scan for @Components Any one implements the Coach interface??? If so, let’s inject them. For example: CricketCoach www.luv2code.com © luv2code LLC Example Application /dailyworkout getDailyWorkout() Web Browser DemoController Coach “Practice fast bowling “Practice fast bowling for 15 minutes” for 15 minutes” www.luv2code.com © luv2code LLC Development Process - Constructor Injection 1. Define the dependency interface and class Step- By-S tep 2. Create Demo REST Controller 3. Create a constructor in your class for injections 4. Add @GetMapping for /dailyworkout www.luv2code.com © luv2code LLC Step 1: Define the dependency interface and class File: Coach.java package com.luv2code.springcoredemo; public interface Coach { File: CricketCoach.java String getDailyWorkout(); } package com.luv2code.springcoredemo; import org.springframework.stereotype.Component; @Component public class CricketCoach implements Coach { @Component annotation @Override public String getDailyWorkout() { marks the class return "Practice fast bowling for 15 minutes"; as a Spring Bean } } www.luv2code.com © luv2code LLC @Component annotation @Component marks the class as a Spring Bean A Spring Bean is just a regular Java class that is managed by Spring @Component also makes the bean available for dependency injection www.luv2code.com © luv2code LLC Step 2: Create Demo REST Controller File: DemoController.java package com.luv2code.springcoredemo; import org.springframework.web.bind.annotation.RestController; @RestController public class DemoController { } www.luv2code.com © luv2code LLC Step 3: Create a constructor in your class for injections File: DemoController.java package com.luv2code.springcoredemo; If you only have one constructor then @Autowired on constructor is optional import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RestController; @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. Spring can figure this out. Later in the course we will cover the case of multiple Coach implementations. www.luv2code.com © luv2code LLC Step 4: Add @GetMapping for /dailyworkout File: DemoController.java package com.luv2code.springcoredemo; 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 { private Coach myCoach; @Autowired public DemoController(Coach theCoach) { myCoach = theCoach; } @GetMapping("/dailyworkout") public String getDailyWorkout() { return myCoach.getDailyWorkout(); } } www.luv2code.com © luv2code LLC Constructor Injection Behind the Scenes © 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; } … } www.luv2code.com © luv2code LLC The Spring Framework will perform operations behind the scenes for you :-) www.luv2code.com © 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 Coach theCoach = new CricketCoach(); Constructor DemoController demoController = new DemoController(theCoach); injection www.luv2code.com © luv2code LLC The “new” keyword … is that it??? You may wonder … Is it just the “new” keyword??? I don’t need Spring for this … I can do this by myself LOL!!! Spring is more than just Inversion of Control and Dependency Injection For small basic apps, it may be hard to see the benefits of Spring www.luv2code.com © luv2code LLC Spring for Enterprise applications Spring is targeted for enterprise, real-time / real-world applications Spring provides features such as Database access and Transactions Later in the course, REST APIs and Web MVC we will build a real-time CRUD REST API with database access. Security You will see the Spring features in action. etc … Good things are coming :-) www.luv2code.com © luv2code LLC Component Scanning m © luv2code LLC Scanning for Component Classes Spring will scan your Java classes for special annotations @Component, etc … Automatically register the beans in the Spring container www.luv2code.com © luv2code LLC Java Source Code RestController that we created in an earlier video Main Spring Boot application class Created by Spring Initializr www.luv2code.com © luv2code LLC Java Source Code File: SpringcoredemoApplication.java package com.luv2code.springcoredemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringcoredemoApplication { public static void main(String[] args) { SpringApplication.run(SpringcoredemoApplication, args); } } www.luv2code.com © luv2code LLC Java Source Code File: SpringcoredemoApplication.java 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 www.luv2code.com © luv2code LLC Java Source Code File: SpringcoredemoApplication.java 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 www.luv2code.com © luv2code LLC Annotations @SpringBootApplication is composed of the following annotations: Annotation Description @EnableAutoConfiguration Enables Spring Boot's auto-configuration support Enables component scanning of current package @ComponentScan Also recursively scans sub-packages Able to register extra beans with @Bean @Configuration or import other configuration classes www.luv2code.com © luv2code LLC Java Source Code File: SpringcoredemoApplication.java package com.luv2code.springcoredemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class SpringcoredemoApplication { public static void main(String[] args) { SpringApplication.run(SpringcoredemoApplication, args); } } Bootstrap your Spring Boot application www.luv2code.com © luv2code LLC Java Source Code File: SpringcoredemoApplication.java Behind the scenes … package com.luv2code.springcoredemo; Creates application context import org.springframework.boot.SpringApplication; and registers all beans import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication Starts the embedded server public class SpringcoredemoApplication { Tomcat etc… public static void main(String[] args) { SpringApplication.run(SpringcoredemoApplication, args); } } Bootstrap your Spring Boot application www.luv2code.com © luv2code LLC More on Component Scanning By default, Spring Boot starts component scanning From same package as your main Spring Boot application Also scans sub-packages recursively This implicitly defines a base search package Allows you to leverage default component scanning No need to explicitly reference the base package name www.luv2code.com © luv2code LLC More on Component Scanning Scans everything in com.luv2code.springcoredemo And any sub-packages (recursively) Any other sub-packages you create Can give them any name Main Spring Boot application class Automatically component scans package and sub-packages www.luv2code.com © luv2code LLC Common Pitfall - Different location By default, Spring Boot will not component scan these packages Only package of main Spring Boot application class 1 and sub-packages Main Spring Boot application class Automatically component scans package and sub-packages www.luv2code.com © luv2code LLC More on Component Scanning Default scanning is fine if everything is under 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 { … } www.luv2code.com © luv2code LLC Setter Injection © luv2code LLC Spring Injection Types Constructor Injection Setter Injection www.luv2code.com © luv2code LLC Inject dependencies by calling setter method(s) on your class www.luv2code.com © luv2code LLC Autowiring Example DemoController Coach Injecting a Coach implementation Spring will scan for @Components Any one implements the Coach interface??? If so, let’s inject them. For example: CricketCoach www.luv2code.com © luv2code LLC Development Process - Setter Injection Step- By-S tep 1. Create setter method(s) in your class for injections 2. Configure the dependency injection with @Autowired Annotation www.luv2code.com © luv2code LLC Step1: Create setter method(s) in your class for injections File: DemoController.java @RestController public class DemoController { private Coach myCoach; public void setCoach(Coach theCoach) { myCoach = theCoach; } … } www.luv2code.com © luv2code LLC Step 2: Configure the dependency injection with Autowired Annotation File: DemoController.java @RestController public class DemoController { private Coach myCoach; @Autowired public void setCoach(Coach theCoach) { myCoach = theCoach; } … } www.luv2code.com © luv2code LLC The Spring Framework will perform operations behind the scenes for you :-) www.luv2code.com © luv2code LLC How Spring Processes your application File: Coach.java File: CricketCoach.java File: DemoController.java public interface Coach { @Component @RestController public class CricketCoach implements Coach { public class DemoController { String getDailyWorkout(); } @Override private Coach myCoach; public String getDailyWorkout() { return "Practice fast bowling for 15 minutes"; @Autowired } public void setCoach(Coach theCoach) { } myCoach = theCoach; } … } Spring Framework Setter injection Coach theCoach = new CricketCoach(); DemoController demoController = new DemoController(); demoController.setCoach(theCoach); www.luv2code.com © luv2code LLC Inject dependencies by calling ANY method on your class Simply give: @Autowired www.luv2code.com © luv2code LLC Step 2: Configure the dependency injection with Autowired Annotation File: DemoController.java @RestController public class DemoController { private Coach myCoach; Can use any method name @Autowired public void doSomeStuff(Coach theCoach) { myCoach = theCoach; } … } www.luv2code.com © luv2code LLC Injection Types - Which one to use? Constructor Injection Use this when you have required dependencies Generally recommended by the spring.io development team as first choice Setter Injection Use this when you have optional dependencies If dependency is not provided, your app can provide reasonable default logic www.luv2code.com © luv2code LLC Field Injection with Annotations and Autowiring © luv2code LLC Spring Injection Types Recommended by the spring.io development team Constructor Injection: required dependencies Setter Injection: optional dependencies Not recommended by the spring.io development team Field Injection www.luv2code.com © luv2code LLC Field Injection … no longer cool In the early days, field injection was popular on Spring projects In recent years, it has fallen out of favor In general, it makes the code harder to unit test As a result, the spring.io team does not recommend field injection However, you will still see it being used on legacy projects www.luv2code.com © luv2code LLC Inject dependencies by setting field values on your class directly (even private fields) Accomplished by using Java Reflection www.luv2code.com © luv2code LLC Step 1: Configure the dependency injection with Autowired Annotation File: DemoController.java package com.luv2code.springcoredemo; 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. Makes the code harder to unit test. www.luv2code.com © luv2code LLC Annotation Autowiring and Qualifiers © luv2code LLC Autowiring DemoController Coach Injecting a Coach implementation Spring will scan @Components Any one implements Coach interface??? If so, let’s inject them … oops which one? www.luv2code.com © luv2code LLC Multiple Coach Implementations Coach CricketCoach TennisCoach BaseballCoach TrackCoach www.luv2code.com © luv2code LLC Multiple Coach Implementations package com.luv2code.springcoredemo.common; package com.luv2code.springcoredemo.common; import org.springframework.stereotype.Component; import org.springframework.stereotype.Component; @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"; } } } } www.luv2code.com © luv2code LLC Umm, we have a little problem …. Parameter 0 of constructor in com.luv2code.springcoredemo.rest.DemoController required a single bean, but 4 were found: - baseballCoach - cricketCoach - tennisCoach - trackCoach … www.luv2code.com © luv2code LLC Solution: Be specific! - @Qualifier package com.luv2code.springcoredemo.rest; 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 } www.luv2code.com © luv2code LLC For Setter Injection If you are using Setter injection, can also apply @Qualifier annotation www.luv2code.com © luv2code LLC Setter Injection - @Qualifier package com.luv2code.springcoredemo.rest; 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 } www.luv2code.com © luv2code LLC @Primary annotation © luv2code LLC Resolving issue with Multiple Coach implementations In the case of multiple Coach implementations We resolved it using @Qualifier We specified a coach by name Alternate solution available … www.luv2code.com © luv2code LLC Alternate solution Instead of specifying a coach by name using @Qualifier I simply need a coach … I don’t care which coach If there are multiple coaches Then you coaches figure it out … and tell me who’s the primary coach www.luv2code.com © luv2code LLC Multiple Coach Implementations package com.luv2code.springcoredemo.common; @Component public class BaseballCoach implements Coach { … import org.springframework.context.annotation.Primary; } import org.springframework.stereotype.Component; @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 { … } www.luv2code.com © luv2code LLC Resolved with @Primary package com.luv2code.springcoredemo.rest; import com.luv2code.springcoredemo.common.Coach; import org.springframework.beans.factory.annotation.Autowired; No need for @Qualifier … @RestController There is a @Primary coach public class DemoController { This example will use TrackCoach private Coach myCoach; @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!"; } } www.luv2code.com © luv2code LLC @Primary - Only one When using @Primary, can have only one for multiple implementations If you mark multiple classes with @Primary … umm, we have a problem Unsatisfied dependency expressed through constructor parameter 0: No qualifying bean of type 'com.luv2code.springcoredemo.common.Coach' available: more than one 'primary' bean found among candidates: [baseballCoach, cricketCoach, tennisCoach, trackCoach] … www.luv2code.com © luv2code LLC Mixing @Primary and @Qualifier If you mix @Primary and @Qualifier @Qualifier has higher priority www.luv2code.com © luv2code LLC Mixing @Primary and @Qualifier package com.luv2code.springcoredemo.rest; 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 { private Coach myCoach; @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!"; } } www.luv2code.com © luv2code LLC Which one: @Primary or @Qualifier? @Primary leaves it up to the implementation classes Could have the issue of multiple @Primary classes leading to an error @Qualifier allows to you be very specific on which bean you want In general, I recommend using @Qualifier More specific Higher priority www.luv2code.com © luv2code LLC Lazy Initialization © luv2code LLC Initialization By default, when your application starts, all beans are initialized @Component, etc … Spring will create an instance of each and make them available www.luv2code.com © luv2code LLC Diagnostics: Add println to constructors Get the name of the class @Component @Component public class CricketCoach implements Coach { public class BaseballCoach implements Coach { public CricketCoach() { public BaseballCoach() { System.out.println("In constructor: " + getClass().getSimpleName()); System.out.println("In constructor: " + getClass().getSimpleName()); } } … … } } @Component @Component public class TrackCoach implements Coach { public class TennisCoach implements Coach { public TrackCoach() { public TennisCoach() { System.out.println("In constructor: " + getClass().getSimpleName()); System.out.println("In constructor: " + getClass().getSimpleName()); } } … … } } www.luv2code.com © luv2code LLC When we start the application … In constructor: BaseballCoach In constructor: CricketCoach In constructor: TennisCoach In constructor: TrackCoach … By default, when your application starts, all beans are initialized Spring will create an instance of each and make them available www.luv2code.com © luv2code LLC Lazy Initialization Instead of creating all beans up front, we can specify lazy initialization A bean will only be initialized in the following cases: It is needed for dependency injection Or it is explicitly requested Add the @Lazy annotation to a given class www.luv2code.com © luv2code LLC Lazy Initialization with @Lazy import org.springframework.context.annotation.Lazy; import org.springframework.stereotype.Component; Bean is only initialized @Component @Lazy if needed for dependency public class TrackCoach implements Coach { injection public TrackCoach() { System.out.println("In constructor: " + getClass().getSimpleName()); } … } Inject cricketCoach Since we are NOT injecting TrackCoach … @RestController public class DemoController { it is not initialized private Coach myCoach; @Autowired … public DemoController(@Qualifier("cricketCoach") Coach theCoach) { In constructor: BaseballCoach myCoach = theCoach; } In constructor: CricketCoach … In constructor: TennisCoach } … www.luv2code.com © luv2code LLC Lazy Initialization To configure other beans for lazy initialization We would need to add @Lazy to each class Turns into tedious work for a large number of classes I wish we could set a global configuration property … www.luv2code.com © luv2code LLC Lazy Initialization - Global configuration File: application.properties spring.main.lazy-initialization=true Once we access REST endpoint /dailywork … Spring will determine dependencies for DemoController … All beans are lazy … no beans are created until needed For dependency resolution Spring creates instance of CricketCoach first … Including our DemoController Then creates instance of DemoController and injects the CricketCoach www.luv2code.com © luv2code LLC Add println to DemoController constructor @Component public class CricketCoach implements Coach { For dependency resolution public CricketCoach() { System.out.println("In constructor: " + getClass().getSimpleName()); Spring creates instance of CricketCoach first … } … } Then creates instance of DemoController and injects the CricketCoach @RestController public class DemoController { private Coach myCoach; @Autowired … public DemoController(@Qualifier("cricketCoach") Coach theCoach) { System.out.println("In constructor: " + getClass().getSimpleName()); In constructor: CricketCoach myCoach = theCoach; } In constructor: DemoController … } … www.luv2code.com © luv2code LLC Lazy Initialization Lazy initialization feature is disabled by default. You should profile your application Advantages before configuring lazy initialization. Avoid the common pitfall of premature optimization. Only create objects as needed 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 May not discover configuration issues until too late Need to make sure you have enough memory for all beans once created www.luv2code.com © luv2code LLC Bean Scopes © luv2code LLC Bean Scopes Scope refers to the lifecycle of a bean How long does the bean live? How many instances are created? How is the bean shared? www.luv2code.com © luv2code LLC Default Scope Default scope is singleton www.luv2code.com © luv2code LLC Refresher: What Is a Singleton? Spring Container creates only one instance of the bean, by default It is cached in memory All dependency injections for the bean will reference the SAME bean www.luv2code.com © luv2code LLC What is a Singleton? Spring @RestController Both point to the same instance public class DemoController { private Coach myCoach; private Coach anotherCoach; @Autowired public DemoController( CricketCoach @Qualifier("cricketCoach") Coach theCoach, @Qualifier("cricketCoach") Coach theAnotherCoach) { myCoach = theCoach; anotherCoach = theAnotherCoach; } … } www.luv2code.com © luv2code LLC Explicitly Specify Bean Scope import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; @Component @Scope(ConfigurableBeanFactory.SCOPE_SINGLETON) public class CricketCoach implements Coach { … } www.luv2code.com © luv2code LLC Additional Spring Bean Scopes Scope Description singleton Create a single shared instance of the bean. Default scope. prototype Creates a new bean instance for each container request. request Scoped to an HTTP web request. Only used for web apps. session Scoped to an HTTP web session. Only used for web apps. Scoped to a global HTTP web session. Only used for web global-session apps. www.luv2code.com © luv2code LLC Prototype Scope Example Prototype scope: new object instance for each injection 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 { … } www.luv2code.com © luv2code LLC Prototype Scope Example Spring Prototype scope: new object instance for each injection @RestController CricketCoach public class DemoController { private Coach myCoach; private Coach anotherCoach; @Autowired CricketCoach public DemoController( @Qualifier("cricketCoach") Coach theCoach, @Qualifier("cricketCoach") Coach theAnotherCoach) { myCoach = theCoach; anotherCoach = theAnotherCoach; } … } www.luv2code.com © luv2code LLC Checking on the scope @RestController public class DemoController { private Coach myCoach; private Coach anotherCoach; Check to see if this is the same bean @Autowired public DemoController( True or False depending on the bean scope @Qualifier("cricketCoach") Coach theCoach, @Qualifier("cricketCoach") Coach theAnotherCoach) { Singleton: True myCoach = theCoach; Prototype: False anotherCoach = theAnotherCoach; } @GetMapping("/check") public String check() { return "Comparing beans: myCoach == anotherCoach, " + (myCoach == anotherCoach); } … } www.luv2code.com © luv2code LLC Bean Lifecycle Methods - Annotations © luv2code LLC Bean Lifecycle Container Bean Dependencies Internal Spring Your Custom Started Instantiated Injected Processing Init Method Bean Is Ready For Use Container Is Shutdown Your Custom Destroy Method www.luv2code.com © luv2code LLC Bean Lifecycle Methods / Hooks You can add custom code during bean initialization Calling custom business logic methods Setting up handles to resources (db, sockets, file etc) You can add custom code during bean destruction Calling custom business logic method Clean up handles to resources (db, sockets, files etc) www.luv2code.com © luv2code LLC Init: method configuration @Component public class CricketCoach implements Coach { public CricketCoach() { System.out.println("In constructor: " + getClass().getSimpleName()); } @PostConstruct public void doMyStartupStuff() { System.out.println("In doMyStartupStuff(): " + getClass().getSimpleName()); } … } www.luv2code.com © luv2code LLC Destroy: method configuration @Component public class CricketCoach implements Coach { 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()); } … } www.luv2code.com © luv2code LLC Development Process Step- By-S tep 1. Define your methods for init and destroy 2. Add annotations: @PostConstruct and @PreDestroy www.luv2code.com © luv2code LLC Configuring Beans with Java Code © luv2code LLC Our New Coach … No sp anno e cial tation s package com.luv2code.springcoredemo.common; public class SwimCoach implements Coach { … } Coach www.luv2code.com © luv2code LLC Development Process Step- By-S tep 1. Create @Configuration class 2. Define @Bean method to configure the bean 3. Inject the bean into our controller www.luv2code.com © luv2code LLC Step 1: Create a Java class and annotate as @Configuration package com.luv2code.springcoredemo.config; import org.springframework.context.annotation.Configuration; @Configuration public class SportConfig { … } www.luv2code.com © luv2code LLC Step 2: Define @Bean method to configure the bean package com.luv2code.springcoredemo.config; 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 { @Bean The bean id defaults public Coach swimCoach() { to the method name return new SwimCoach(); } } www.luv2code.com © luv2code LLC Step 3: Inject the bean into our controller package com.luv2code.springcoredemo.rest; 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; @RestController Inject the bean using public class DemoController { the bean id private Coach myCoach; @Autowired public DemoController(@Qualifier("swimCoach") Coach theCoach) { System.out.println("In constructor: " + getClass().getSimpleName()); myCoach = theCoach; } … } www.luv2code.com © luv2code LLC Use case for @Bean You may wonder … Using the “new” keyword … is that it??? Why not just annotate the class with @Component??? www.luv2code.com © luv2code LLC Use case for @Bean Make an existing third-party class available to Spring framework 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 www.luv2code.com © luv2code LLC Real-World Project Example Our project used Amazon Web Service (AWS) to store documents Amazon Simple Storage Service (Amazon S3) Amazon S3 is a cloud-based storage system can store PDF documents, images etc We wanted to use the AWS S3 client as a Spring bean in our app www.luv2code.com © luv2code LLC Real-World Project Example The AWS S3 client code is part of AWS SDK We can’t modify the AWS SDK source code We can’t just add @Component However, we can configure it as a Spring bean using @Bean www.luv2code.com © luv2code LLC Configure AWS S3 Client using @Bean package com.luv2code.springcoredemo.config; … 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() { // Create an S3 client to connect to AWS S3 ProfileCredentialsProvider credentialsProvider = ProfileCredentialsProvider.create(); Region region = Region.US_EAST_1; S3Client s3Client = S3Client.builder().region(region).credentialsProvider(credentialsProvider).build(); return s3Client; } } www.luv2code.com © luv2code LLC Inject the S3Client as a bean package com.luv2code.springcoredemo.services; import software.amazon.awssdk.services.s3.S3Client; … @Component public class DocumentsService { private S3Client s3Client; @Autowired public DocumentsService(S3Client theS3Client) { s3Client = theS3Client; } … } www.luv2code.com © luv2code LLC Store our document in S3 package com.luv2code.springcoredemo.services; import software.amazon.awssdk.services.s3.S3Client; … @Component public class DocumentsService { private S3Client s3Client; @Autowired public DocumentsService(S3Client theS3Client) { s3Client = theS3Client; } public void processDocument(Document theDocument) { // get the document input stream and file size … // Store document in AWS S3 // Create a put request for the object PutObjectRequest putObjectRequest = PutObjectRequest.builder().bucket(bucketName).key(subDirectory + "/" + fileName).acl(ObjectCannedACL.BUCKET_OWNER_FULL_CONTROL).build(); // perform the putObject operation to AWS S3... using our autowired bean s3Client.putObject(putObjectRequest, RequestBody.fromInputStream(fileInputStream, contentLength)); } } www.luv2code.com © luv2code LLC Wrap Up We could use the Amazon S3 Client in our Spring application The Amazon S3 Client class was not originally annotated with @Component However, we configured the S3 Client as a Spring Bean using @Bean It is now a Spring Bean and we can inject it into other services of our application Make an existing third-party class available to Spring framework www.luv2code.com © luv2code LLC