Spring core in spring framework
Inversion of Control
Spring IoC (Inversion of Control) Container is the core of Spring Framework. It creates the objects, configures and assembles their dependencies, manages their entire life cycle. The Container uses Dependency Injection(DI) to manage the components that make up the application.
It gets the information about the objects from a configuration file(XML) or Java Code or Java Annotations and Java POJO class. These objects are called Beans.
There are 2 types of IoC containers:
The followings are some of the main features of Spring IoC,
Creating Object for us,
Managing our objects,
Helping our application to be configurable,
Managing dependencies
Dependency Injection
Dependency injection is a pattern we can use to implement IoC. Dependency Injection is a core principle of the Spring Framework. It's a design pattern that promotes loose coupling between objects by removing the responsibility of creating and managing dependencies from the objects themselves. Instead, the Spring container takes on this responsibility.
Types of IoC Container
BeanFactory
BeanFactory interface is the simplest container providing an advanced configuration mechanism to instantiate, configure, and manage the life cycle of beans.
BeanFactory uses Beans and their dependencies metadata to create and configure them at run-time. BeanFactory loads the bean definitions and dependency amongst the beans based on a configuration file (XML).
BeanFactory does not support Annotation-based configuration whereas ApplicationContext does.
ApplicationContext
- ApplicationContext is the sub-interface of BeanFactory. It is used when we are creating an enterprise-level application or web application. ApplicationContext is the superset of BeanFactory, whatever features provided by BeanFactory are also provided by ApplicationContext.
Note: It is because of these additional features, developers prefer to use ApplicationContext over BeanFactory. Spring – BeanFactory : It provides basic functionalities and is recommended for use for lightweight applications like mobile and applets.
ApplicationContext Implementation Classes
There are different types of Application containers provided by Spring for different requirements as listed below which later onwards are described alongside with declaration, at lastly providing an example to get through the implementation part with the pictorial aids. Containers are as follows:
AnnotationConfigApplicationContext container
AnnotationConfigWebApplicationContext
XmlWebApplicationContext
FileSystemXmlApplicationContext
ClassPathXmlApplicationContext
Beans Scope in Spring core
Singleton Scope
The default scope in Spring, singleton, ensures that a single instance of a bean is created per Spring IoC container. This instance is shared across the entire container.
@Component
@Scope("singleton")
public class SingletonBean {
public SingletonBean() {
System.out.println("Singleton instance created");
}
}
Prototype Scope
In the prototype scope, a new instance of the bean is created every time it is requested from the Spring container.
@Component
@Scope("prototype")
public class PrototypeBean {
public PrototypeBean() {
System.out.println("Prototype instance created");
}
}
Request Scope
The request scope is specific to web-aware Spring applications. A new bean instance is created for each HTTP request.
@Component
@Scope("request")
public class RequestBean {
public RequestBean() {
System.out.println("Request instance created");
}
}
Session Scope
Let’s understand what is session first
Request 1: User visits the website for the first time.
- The server creates a session and sends the Session ID back to the client in the response.
Request 2: User sends another request (e.g., navigating to a different page).
The client includes the Session ID in the request.
The server identifies the session using the Session ID and retrieves the stored data for that user.
Request N: User continues interacting with the website.
- Session data is maintained until the session expires or is explicitly terminated.
How Does HTTP Work?
HTTP is Stateless: Each HTTP request sent by a client (browser) to the server is independent of any previous request. The server does not retain information about the user between requests.
Problem: In a stateless protocol, there is no built-in way to remember a user's data (like login status or a shopping cart) across requests.
Session Scope in Spring
In Spring, session scope refers to the lifecycle of a bean being tied to an HTTP session. A new instance of a session-scoped bean is created for each HTTP session, and it is shared across all requests made within that session.
Once the session expires or the user logs out, the session-scoped bean is destroyed.
Application Scope
The application scope is also specific to web applications. It creates a single bean instance for the lifecycle of a ServletContext.
@Component
@Scope("application")
public class ApplicationBean {
public ApplicationBean() {
System.out.println("Application instance created");
}
}
Stereotype Annotation
Let’s understand Annotations first:
- Spring Annotations are a form of metadata that provides data about a program. Annotations are used to provide supplemental information about a program.
Stereotype Annotation:
Spring Framework provides us with some special annotations. These annotations are used to create Spring beans automatically in the application context. @Component annotation is the main Stereotype Annotation. There are some Stereotype meta-annotations which is derived from @Component those are
@Service: We specify a class with @Service to indicate that they’re holding the business logic. Besides being used in the service layer, there isn’t any other special use for this annotation. The utility classes can be marked as Service classes.
@Repository: We specify a class with @Repository to indicate that they’re dealing with CRUD operations, usually, it’s used with DAO (Data Access Object) or Repository implementations that deal with database tables.
@Controller: We specify a class with @Controller to and responsible to indicate that a class serves as a controller, handle user requests and return the appropriate response. It is mostly used with REST Web Services.
Examples of Bean Scope and Stereotype annotation
- Main Class
package com.codefreak;
import org.springframework.context.ApplicationContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.codefreak.config.AppConfig;
import com.codefreak.service.Item;
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
Item samsungItem1 = (Item) context.getBean("getItem1");
// printing value
System.out.println("Item 1 Name : " + samsungItem1.getItemName());
System.out.println("Item 2 Cost : " + samsungItem1.getItemCost());
Item samsungItem2 = (Item) context.getBean("getItem1");
// check both beans, output will be true as scope is singleton
if (samsungItem1 == samsungItem2) {
System.out.println("Both beans are same, as same instance is used acrossed the entire container");
}
// checking prototype
Item redmiItem1 = (Item) context.getBean("getItem1");
Item redmiItem2 = (Item) context.getBean("getItem2");
if (redmiItem1 != redmiItem2) {
System.out.println("Both beans are not same, as there are different instace created for this bean");
}
SpringApplication.run(MainApplication.class, args);
}
}
- App Config
package com.codefreak.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.codefreak.service.Item;
@Configuration
public class AppConfig {
@Bean
@Scope("singleton")
public Item getItem1() {
Item item = new Item("Samsung A12", 30000);
return item;
}
@Bean
@Scope("prototype")
public Item getItem2() {
Item item = new Item("Redmi", 40000);
return item;
}
@Bean
@Scope("request")
public Item getItem3() {
Item item = new Item("IQOO", 70000);
return item;
}
}
- Item service
package com.codefreak.service;
public class Item {
private String itemName;
private int itemCost;
public Item(String itemName, int itemCost) {
this.itemName = itemName;
this.itemCost = itemCost;
}
public String getItemName() {
return itemName;
}
public void setItemName(String itemName) {
this.itemName = itemName;
}
public int getItemCost() {
return this.itemCost;
}
public void setItemCost(int itemCost) {
this.itemCost = itemCost;
}
}
- Item Controller
package com.codefreak.controller;
import org.springframework.context.ApplicationContext;
import org.springframework.web.bind.annotation.RestController;
import com.codefreak.service.Item;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
@RestController
public class ItemsController {
private final ApplicationContext context;
public ItemsController(ApplicationContext context) {
this.context = context;
}
@GetMapping(value = "/")
public ResponseEntity<?> getItem() {
Item iqooItem = (Item) context.getBean("getItem3");
System.out.println(iqooItem.hashCode());
return new ResponseEntity<>(iqooItem, HttpStatus.OK);
}
}
Dependency Injection
Setter Dependency Injection
package com.codefreak.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.codefreak.models.Car;
// Setter dependency injection
@Component("CarService")
public class CarService {
private Car car;
@Autowired
public void setCar(Car car) {
this.car = car;
}
public Car getCarDetails() {
car.setCarName("Mercedes");
car.setCarCost(400000);
return car;
}
}
Field Dependency Injection
package com.codefreak.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.codefreak.models.Car;
// field level dependency injection
@Component("CarService")
public class CarService {
@Autowired
private Car car;
public Car getCarDetails() {
car.setCarName("Mercedes");
car.setCarCost(400000);
return car;
}
}
Constructor Dependency Injection
package com.codefreak.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import com.codefreak.models.Car;
// constructor dependency injection
@Component("CarService")
public class CarService {
private final Car car;
public CarService(Car car) {
this.car = car;
}
public Car getCarDetails() {
car.setCarName("Mercedes");
car.setCarCost(400000);
return car;
}
}
Some config files before implementing this dependency injection
- Appconfig
package com.codefreak.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import com.codefreak.service.Item;
@Configuration
@ComponentScan(basePackages = "com.codefreak")
public class AppConfig {
}
- Main class
package com.codefreak;
import org.springframework.context.ApplicationContext;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.codefreak.config.AppConfig;
import com.codefreak.service.CarService;
import com.codefreak.service.Item;
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
// setter dependecy injection demo
CarService carService = (CarService) context.getBean("CarService", CarService.class);
System.out.println(carService.getCarDetails().getCarName());
System.out.println(carService.getCarDetails().getCarCost());
SpringApplication.run(MainApplication.class, args);
}
}