- Using @Component over class – To create app specific bean
- Using @Configuration over class and @Bean over method – To create bean from third party class added as JAR dependency. You cannot add component in added JAR class files.
Dependency injection is basically providing the objects that an object needs (its dependencies) instead of having it construct them itself.
- Constructor Injection – Dependency provided as parameter to constructor. This ensures Object is fully initialized upon creation and promoting immutability
- Setter Injection – Setter injection is having independent setter for each class attributes and calling setter method while initializing. Setter Injection allow more flexibility by allowing dependencies to be set or changed after object creation.
- Field Injection – Field injection is old method of injecting dependencies by specifying the @Autowired over field name. It works based on Java reflection and it is not recommended as it makes code more difficult to test
Stereotype – idea of a particular type of person or thing.
Spring comes with 4 Stereotype annotations as below.
@Component is a generic stereotype for any Spring-managed component. @Repository, @Service, @Controller are specializations of @Component for more specific use cases (in the persistence, service, and presentation layers, respectively).
- @Component general-purpose stereotype annotation indicating that the class is a spring component.
- @Controller @Controller annotation indicates that a particular class serves the role of a controller. The dispatcher scans the classes annotated with @Controller and detects methods annotated with @RequestMapping annotations within them. We can use @RequestMapping on/in only those methods whose classes are annotated with @Controller
- @Repository
stereotype for persistence layer. @Repository’s job is to catch platform specific exceptions and re-throw them as one of Spring’s unified unchecked exception. By masking the platform specific exception to spring unified exception it helps 1.Providing higher level of Abstraction for User 2.By throwing unchecked exception it prevents user from adding unnecessary boiler plate for exception handling by means of try catch blocks
- @Service – @Service beans hold the business logic and call methods in the repository layer.
Note : Spring may add special functionalities for @Service, @Controller and @Repository based on their layering conventions.
How it is internally is all 3 are marked with @Component.@CompnentScan only scans @Component and does not look for @Controller, @Service and @Repository in general. They are scanned because they themselves are annotated with @Component.
@Component
public @interface Service {
….
}
@Component
public @interface Repository {
….
}
@Component
public @interface Controller {
…
}
Application would fail to start with error message Require Single bean but N beans found.
This could be addressed in two ways
- Using @Qualifier annotation
- Using @Primary annotation
@Qualifier annotation takes bean Id as value to uniquely identify a bean.
StudentController.java
public class StudentController{
.
@Autowired
public StudentController(@Qualifier("student") Person person){
.
}
}
Student.java
@Component
public class Student implements Person{
.
}
@Primary annotation works by giving first preference to bean which is marked with @Primary annotation. If two beans are marked with @Primary annotation then Spring boot throws an error. If both @Primary and @Qualifier annotation is used, then @Qualifier annotation takes preference.
@Qualifier takes precedence over @Primary annotation.
Unlike the bean which are marked with @Component are loaded at startup when the app start, @Lazy annotation does the bean loading only when it is needed.
This can be Configured globally as well by specifying in application.properties as below. If your application has lot of components and because of this the app takes long time to start then its better to use this.
application.properties
spring.main.lazy-initialization=true
Note: If you use @Lazy annotation along with @Restcontroller it may lead to timeout issues.
Report.java
public interface Report {
String generateReport();
}
PDFReport.java
@Primary
@Component
public class PDFReport implements Report{
public PDFReport() {
System.out.println("Loaded PDFReport");
}
@Override
public String generateReport() {
return "PDF Report";
}
}
excelReport.java
@Component
public class excelReport implements Report{
public excelReport() {
System.out.println("Loading Excel Report");
}
@Override
public String generateReport() {
return "Excel Report";
}
}
textReport.java
@Component
@Lazy
public class textReport implements Report{
public textReport() {
System.out.println("Loading textReport Report");
}
@Override
public String generateReport() {
return "Text Report";
}
}
GenerateReport.java
@RestController
public class GenerateReport {
private Report report;
public GenerateReport(@Qualifier("excelReport") Report report){
this.report = report;
}
@GetMapping("/generateReport")
public String generateReport(){
return "Printing report in "+ report.generateReport() + " format";
}
}
Excel Report format would be printed as @Qualifier would take precedence
Output in Browser(http://localhost:8080/generateReport)
Loading Excel Report
Loaded PDFReport
Printing report in Excel Report format
Singleton – Only one bean for Container, served same bean for every access
Prototype – New bean for every access
Request – New bean for every request
Session – Only one bean for Session
Application – Only one bean for Application
Websocket –
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
excelReport bean with Singleton scope returning same bean every access
excelReport.java
@Component
public class excelReport implements Report{
.
.
}
GenerateReport.java
@RestController
public class GenerateReport {
private Report report1;
private Report report2;
public GenerateReport(@Qualifier("excelReport") Report report1, @Qualifier("excelReport") Report report2){
this.report1 = report1;
this.report2 = report2;
}
@GetMapping("/checkBean")
public String checkBean(){
if(report1 == report2){
return "Bean are Same";
}else{
return "Bean are Different";
}
}
}
Output(http://localhost:8080/checkBean)
Bean are Same
pdfReport bean with Prototype scope returning different bean on every access
PDFReport.java
@Primary
@Component
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public class PDFReport implements Report{
.
.
}
GenerateReport.java
@RestController
public class GenerateReport {
private Report report1;
private Report report2;
public GenerateReport(Report report1, Report report2){
this.report1 = report1;
this.report2 = report2;
}
@GetMapping("/checkBean")
public String checkBean(){
if(report1 == report2){
return "Bean are Same";
}else{
return "Bean are Different";
}
}
}
Output(http://localhost:8080/checkBean)
Bean are Different
Objects for 3rd party JAR are created using @Configuration at class level and using @Bean at method level. This is one other way to create bean other than @Component as
we wont be able to change the source of 3rd party class files.
- Using @Configuration and @Bean annotation
- Using @Configurable refer here
- using AutowireCapableBeanFactory
Let take the below code
public class MyBean
{
@Autowired
private AnotherBean anotherBean;
}
MyBean obj = new MyBean();
I have a class doStuff in which the obj of MyBean created using new operator needs to be injected. To do this use AutowireCapableBeanFactory and call autowireBean method with beanFactory reference.
private @Autowired AutowireCapableBeanFactory beanFactory;
public void doStuff() {
MyBean obj = new MyBean();
beanFactory.autowireBean(obj);
//obj will now have its dependencies autowired.
}
@Inject specified in javax.inject.Inject annotations is part of the Java CDI (Contexts and Dependency Injection) standard introduced in Java EE 6 (JSR-299). Spring has chosen to support using @Inject synonymously with their own @Autowired annotation.@Autowired is Spring’s own (legacy) annotation. @Inject is part of a new Java technology called CDI that defines a standard for dependency injection similar to Spring. In a Spring application, the two annotations work the same way as Spring has decided to support some JSR-299 annotations in addition to their own.
@RequestMapping – All incoming requests are handled by the Dispatcher Servlet and it routes them through the Spring framework. When the Dispatcher Servlet receives a web request, it determines which controllers should handle the incoming request. Dispatcher Servlet initially scans all the classes that are annotated with the @Controller annotation. The dispatching process depends on the various @RequestMapping annotations declared in a controller class and its handler methods.The @RequestMapping annotation is used to map the web request onto a handler class (i.e. Controller) or a handler method and it can be used at the Method Level or the Class Level.
Example – for the URL http://localhost:8080/ProjectName/countryController/countries
@Controller
@RequestMapping(value = "/countryController")
public class CountryController {
@RequestMapping(value = "/countries", method = RequestMethod.GET, headers = "Accept=application/json")
public List getCountries() {
// Some Business Logic
}
Annotations |
Equivalent |
@GetMapping |
@RequestMapping(method = RequestMethod.GET) |
@PostMapping |
@RequestMapping(method = RequestMethod.POST) |
@PutMapping |
@RequestMapping(method = RequestMethod.PUT) |
@DeleteMapping |
@RequestMapping(method = RequestMethod.DELETE) |
@PatchMapping |
@RequestMapping(method = RequestMethod.PATCH) |
@RequestParam – By using RequestParam we can get parameters to the method
@RequestMapping(value = "/display", method = RequestMethod.GET)
public String showEmployeeForm(@RequestParam("empId") String empId) {
// Some Business Logic
}
@Pathvariable – @PathVariable is to obtain some placeholder from the URI
If empId and empName as parameters to the method showEmployeeForm() by using the @PathVariable annotation. For e.g.: /employee/display/101/Mux
empId = 101
empName = Mux
@Controller
@RequestMapping(value = "/employee")
public class EmployeeController {
@RequestMapping(value = "/display/{empId}/{empName}")
public ModelAndView showEmployeeForm(@PathVariable String empId, @PathVariable String empName) {
// Some Business Logic
}
}
@RequestParam annotation has following attributes
http://localhost:8080/springmvc/hello/101?param1=10¶m2=20
public String getDetails(
@RequestParam(value="param1", required=true) String strParam1,
@RequestParam(value="param2", required=false, defaultValue = "John") String strParam2){
...
}
@PathVariable is always considered required and cannot be null whereas @RequestParam could be null
@RequestParam annotation can specify default values if a query parameter is not present or empty by using a default Value attribute, provided the required attribute is false. . If a corresponding path segment is missing, Spring will result in a 404 Not Found error.
defaultValue |
This is the default value as a fallback mechanism if request is not having the value or it is empty. i.e param1 |
name |
Name of the parameter to bind i.e param1 |
required |
Whether the parameter is mandatory or not. If it is true, failing to send that parameter will fail. i.e false |
value |
This is an alias for the name attribute i.e param1 |
@PathVariable is to obtain some placeholder from the URI (Spring call it an URI Template) and @RequestParam is to obtain an parameter from the URI as well.@PathVariable annotation has only one attribute value for binding the request URI template. It is allowed to use the multiple @PathVariable annotation in the single method. But, ensure that no more than one method has the same pattern.Annotation which indicates that a method parameter should be bound to a name-value pair within a path segment. Supported for RequestMapping annotated handler methods.
localhost:8080/person/Tom;age=25;height=175 and Controller:
@GetMapping("/person/{name}")
@ResponseBody
public String person(
@PathVariable("name") String name,
@MatrixVariable("age") int age,
@MatrixVariable("height") int height) {
// ...
}
In such case it would fail at start telling expected 1 bean but found n bean
We can use @Qualifier annotation where we can decide the bean to be loaded based on the beanId. beanId is always the component name with camel casing
public class GenerateReport {
private Report report1;
private Report report2;
public GenerateReport(@Qualifier("excelReport") Report report1, Report report2){
this.report1 = report1;
this.report2 = report2;
}
}
@PostConstruct and @PreDestroy
@Component
public class excelReport implements Report{
public excelReport() {
System.out.println("Loading Excel Report");
}
@Override
public String generateReport() {
return "Excel Report";
}
@PreDestroy
public void doCleanUpStuff(){
System.out.println("Disposing ExcelReport....: Prints when Server Stops");
}
@PostConstruct
public void doStartUpStuff(){
System.out.println("Completed Initialization ExcelReport...: Prints when Server Starts");
}
}
Output – When Server Starts
Completed Initialization ExcelReport...: Prints when Server Starts
Output – When Server Stops
Disposing ExcelReport....: Prints when Server Stops
- @RequestBody and @ResponseBody used in controller to implement smart object serialization and deserialization. They help you avoid boilerplate code by extracting the logic of message conversion and making it an aspect. Other than that they help you support multiple formats for a single REST resource without duplication of code.
- If you annotate a method with @ResponseBody, spring will try to convert its return value and write it to the http response automatically.
- If you annotate a methods parameter with @RequestBody, spring will try to convert the content of the incoming request body to your parameter object on the fly.
When Spring loads your bean definitions, and has been configured to look for @Transactional annotations, it will create these proxy objects around your actual bean. These proxy objects are instances of classes that are auto-generated at runtime. The default behaviour of these proxy objects when a method is invoked is just to invoke the same method on the “target” bean (i.e. your bean).
Refere here