Spring Framework and Spring boot
Table of contents
- Spring Core:
- Spring Boot:
- ORM(Object-Relational Mapping) Theory:
- Spring Hibernate Configuration:
- Spring Data JPA:
- Query DSL:
- @Query Annotation:
- RESTful Web Services/API:
- Rest APIs are stateless:
- REST APIs, the data is returned in a representational state:
- @ResponseBody:
- @Autowired:
- @GetMapping(""):
- Java Object to JSON Conversion:
- Optional:
- Jackson JSON and Jackson XML:
- Produces Attribute:
- Consumes Attribute:
- Request Body and Response Body:
- Components in Spring Boot:
- Important Points:
- Exception Handling in Spring Boot:
- Handling Specific Exceptions:
- Custom Error Format:
Spring Core:
Spring is one of the most powerful frameworks in Java. Of course, there are many other frameworks like Struts and EJB, and on the database side, we have JPA. However, with the help of Spring, we can achieve everything in one framework.
One important thing about Spring is that it focuses on POJOs (Plain Old Java Objects).
We can build scalable applications using EJB, but the components used in EJB are often heavy in terms of size and the services they require.
Spring is a combination of different modules. Some of the core Spring modules are listed below:
Spring Core: Provides Dependency Injection.
Spring MVC: Used for Web Development.
Spring REST: For building REST APIs.
Maven: A build tool that manages dependencies for your project.
What is Spring and What is Spring Boot?
Spring is a Java framework used to build enterprise-ready applications. When developing an application using the Spring framework, we need to do a lot of configuration. For example, if we want to use Hibernate in our Spring application, we need to configure it extensively and add numerous libraries and JAR files based on our requirements. Due to this, developers need to focus on both configuration and convention, which can be quite hectic.
To simplify this process, developers decided to create a solution that would allow them to focus only on conventions (rather than configurations). This led to the creation of Spring Boot.
Bean Factory:
In the Spring framework, we usually need to configure everything. For example, if we need to create an object for a class, we have to configure it using one of the following methods:
XML-based configuration
Java-based configuration
Annotation-based configuration
The BeanFactory is a basic container in Spring that provides the fundamental features for creating and managing beans(classes). It is typically used for lightweight applications where you don’t need the full functionality provided by the ApplicationContext
.
please Note that spring framework is different and spring boot is different. spring boot is build top on the spring framework only. In spring framework we have to do configuration and in Spring boot configuration all taken care by springboot. dont get confused. above i have explained about spring framework.
XML-Based Configuration:
- Create a new Spring bean configuration file (e.g.,
spring.xml
).
Old approach using XmlBeanFactory
(now deprecated):
BeanFactory factory = new XmlBeanFactory(new FileSystemResource("spring.xml")); // It will read the spring.xml file
Alien obj = (Alien) factory.getBean("Alien"); // The getBean method will return the bean we defined in spring.xml
obj.code();
In the above code, the XmlBeanFactory
method was deprecated. So, we can achieve the same functionality using ApplicationContext
.
Updated approach using ApplicationContext
:
ApplicationContext factory = new ClassPathXmlApplicationContext("spring.xml"); // It will read the spring.xml file from the classpath
Alien obj = (Alien) factory.getBean("Alien"); // The getBean method will return the bean we defined in spring.xml
obj.code();
Note: ApplicationContext
is an interface, so we cannot instantiate it directly. Instead, we create an object of one of its implemented classes, such as ClassPathXmlApplicationContext
or FileSystemXmlApplicationContext
.
Spring Container:
Inside the JVM, a container is available, and all the beans and processes that we create are stored inside that container.
ApplicationContext factory = new ClassPathXmlApplicationContext("spring.xml"); // This line provides the container within the JVM.
In the above code, we load the spring.xml
file. So, whatever beans we define inside the spring.xml
file, their objects will be created in the Spring container.
For example, if we declare 5 beans inside the spring.xml
file, those 5 objects will be created inside the Spring container.
If you are creating two different objects but referring to the same bean, the Spring container will return the same object reference for both objects. It won’t create a new object.
This is why we say that all Spring beans follow the Singleton pattern. The Singleton pattern means that only one instance of the object will be created. By default, Spring follows the Singleton design pattern.
What if you want to create two different objects and avoid the Singleton pattern?
To achieve this, you can use the Prototype scope. For example:
spring.xml:
<bean id="myBean" class="com.example.MyClass" scope="prototype"/>
In this case, Spring will create a new object every time the bean is requested, instead of returning the same object reference.
Singleton vs Prototype:
When creating a bean, apart from the id
and class
, we have another attribute called scope
.
Singleton Scope:
spring.xml:
<bean id="alien" class="com.lakshmi.SpringDemo.Alien" scope="singleton"/>
If we set the scope to singleton, only one object will be created. Even if we call the bean multiple times, it will return the same object reference.
Prototype Scope:
spring.xml:
<bean id="alien" class="com.lakshmi.SpringDemo.Alien" scope="prototype"/>
If we set the scope to prototype, each time we call the bean, a new object will be created. For example, if we call the same bean 10 times, 10 different objects will be created.
Example:
package com.lakshmi.SpringDemo;
public class Alien {
int age;
public Alien() {
System.out.println("Alien object created");
}
public void code() {
System.out.println("Alien is coding");
}
}
public static void main(String[] args) {
// ApplicationContext factory = new ClassPathXmlApplicationContext("spring.xml"); // It will read spring.xml file
ApplicationContext factory = new ClassPathXmlApplicationContext("spring.xml");
Alien obj1 = (Alien) factory.getBean("alien"); // The getBean method will call the bean defined in spring.xml
obj1.code();
obj1.age = 15;
System.out.println(obj1.age);
Alien obj2 = (Alien) factory.getBean("alien"); // The getBean method will call the bean defined in spring.xml
obj2.code();
System.out.println(obj2.age);
}
For Singleton scope output will be:
Alien object created //Here constructor is called only one time
Alien is coding
15
Alien is coding
15
For Prototype Scope, the output will be:
Alien object created
Alien is coding
15
Alien object created //Here constructor calling each time when getBean is called
Alien is coding
0
Singleton Scope:
Only one instance of the bean is created.
The object is shared across the application.
The constructor is called once.
Prototype Scope:
A new instance is created every time
getBean()
is called.The object is not shared, and changes in one instance will not affect the other.
The constructor is called each time.
So, in Spring Core, we have these two main scopes: singleton and prototype. There are other scopes in different Spring modules, but these are the core scopes in Spring Core.
Setter Injection:
Setter Injection is done using <property>
tags inside the Spring XML file.
We can assign the value of age
in the spring.xml
file using the <property>
tag.
To assign a value to the variable, use the following code in the spring.xml
file:
<bean id="alien" class="com.lakshmi.SpringDemo.Alien" scope="prototype">
<property name="age" value="15"></property>
</bean>
This code will assign the value 15
to the age
property of the Alien
class. But what if we want to inject a reference to another object? For example, if we want to use an object of the Laptop
class inside the Alien
class, we can use the following code in the spring.xml
file.
Here, we define the Laptop
object inside the Alien
class using the ref
tag:
<bean id="alien" class="com.lakshmi.SpringDemo.Alien" scope="prototype">
<property name="age" value="15"></property>
<property name="laptop" ref="laptop"></property>
</bean>
<bean id="laptop" class="com.lakshmi.SpringDemo.Laptop"></bean>
Constructor Injection:
If we want to use constructor injection, we need to use the <constructor-arg>
tag in the spring.xml
file.
<bean id="alien" class="com.lakshmi.SpringDemo.Alien" scope="prototype">
<constructor-arg value="15"></constructor-arg>
</bean>
Setter Injection → Uses the
<property>
tag.Constructor Injection → Uses the
<constructor-arg>
tag.
Autowire:
autowire="byName" → Spring will search for a bean by its name.
autowire="byType" → Spring will search for a bean by its type.
If we use autowire="byType" and there are two beans of the same type in the XML file, Spring will get confused about which bean to inject. To resolve this, we can use the primary tag.
- primary="true" → If you set this attribute on a bean, it will be treated as the primary bean, and Spring will inject it when there is ambiguity.
Spring Boot:
Spring Boot is an extension of the Spring framework. It is not a different framework; instead, Spring Boot uses the Spring framework internally.
Benefits of Spring Boot:
Rapid Application Development:
Spring Boot helps in building applications quickly and easily. It simplifies development by using the Spring framework internally, making it less complicated for developers.Managing Dependencies:
Spring Boot provides many starter templates that include all the necessary dependencies for a specific task.For example:
To work with JDBC, we use
spring-boot-starter-JDBC
. This includes everything needed to connect to a database.To work with JPA, we use
spring-boot-starter-JPA
. This includes everything needed for JPA tasks.To test applications, we use
spring-boot-starter-test
, which includes testing tools like JUnit and Mockito.
Without Spring Boot, developers need to configure each dependency manually, which can be very time-consuming and hectic.
Auto-Configuration:
Spring Boot handles most configurations automatically. For example, if you want to use Hibernate, you just need to add the required dependency, and Spring Boot will take care of the rest of the configuration.Embedded Server:
Traditionally, to deploy an application, you had to:Write the application code.
Create a WAR file for the application.
Deploy the WAR file on a server (like Tomcat, JBoss, etc.).
With Spring Boot, you don’t need to create a WAR file. Instead, you create a JAR file, and the server (like Tomcat) is embedded inside the JAR file itself. This allows you to deploy the application directly and makes it production-ready.
Dependency Injection:
For example, we have created a Student
class, and we are creating an object of the Student
class manually using Student s = new Student()
. Here, we are creating the object manually.
If we have 100 classes, and all the classes are interrelated, manually creating objects using the new
keyword becomes very difficult, and the application becomes tightly coupled.
This is where the concept of Dependency Injection comes in. With Dependency Injection, we don’t manually create objects using the new
keyword. Instead, Spring will store all the classes (called beans) and their objects in a container.
We simply tell Spring that this class needs the object of that class, and that class needs the object of this class, and Spring will take care of the rest. In other words, Spring will provide the required objects automatically, so we don’t have to create them manually.
Inversion of Control:
Using the same example as above, in Inversion of Control (IoC), we don't create any objects ourselves. Instead, Spring will create the objects and provide them to us when needed. In this case, we give control to Spring for creating the objects and handling all configurations. We don’t need to worry about the setup or configurations.
This process of giving control to the Spring framework, rather than doing it ourselves, is called Inversion of Control.
Spring Initializer:
By using Spring.io, we can create a project and add all the required dependencies at the same time. To do this, we can simply go to Google, search for Spring Initializr, and use it to generate a project with the dependencies we need.
Spring Boot Starters:
There are many Spring Boot starters available in Spring Boot. Based on our requirements, we can use the respective starters. For example:
spring-boot-starter-web: This starter includes everything needed for a web application, including Tomcat as the default embedded server.
spring-boot-starter-test: This starter is used for testing the application.
And there are many other starters available, depending on the needs of your application.
Just by adding dependencies while creating the project, Spring Boot takes care of the remaining configurations. Spring.io itself creates the pom.xml
file and includes all the necessary configurations. That’s the advantage of using Spring Boot.
After creating a Spring Boot project, the @SpringBootApplication
annotation will be present by default. If we open this annotation, we can see all the annotations that are available for the project.
SpringApplication.run(SpringFrameworkDemoApplication.class, args);
This above method starts our Spring Boot application.
We can also exclude some classes that we don’t need in our application. For example, if we don’t need Hibernate, we can exclude it using the @EnableAutoConfiguration
annotation.
@EnableAutoConfiguration(exclude = {HibernateJpaAutoConfiguration.class})
Component Scan:
The @ComponentScan
annotation scans all the classes in a particular package and stores the objects of those classes inside the container. If we have classes in a different package, we need to add @ComponentScan
for that package as well.
getBean():
The getBean()
method belongs to an interface called ApplicationContext.
@Component:
If we apply the @Component
annotation to a class, it marks that class as a Spring bean, which means Spring is responsible for creating and managing the object of that class.
Example:
@SpringBootApplication
public class FirstprojectApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(FirstprojectApplication.class, args);
Alien a = context.getBean(Alien.class);
a.code();
}
}
In the above code, the run()
method is called from the SpringApplication
class, which implements the ApplicationContext
interface. I am collecting the object of the ApplicationContext
and calling the getBean()
method to fetch the Alien
class instance.
The Alien
class is annotated with @Component
as shown below:
@Component
public class Alien {
public void code() {
System.out.println("Lakshmi");
}
}
In this example, Spring manages the Alien
class as a bean and provides it when requested.
If we are not giving @Component
Annotation to Alien class then it will throw an Exception called NoSuchBeanDefinitionException.
@Autowired:
With @Autowired
, we are not manually creating the object. Instead, the Spring framework will automatically create the object for us.
For @Autowired
to work, the class must be annotated with @Component
(or any of its specialized annotations like @Service
, @Repository
, etc.), so Spring can recognize it as a bean and manage its lifecycle.
Note: both
@Service
and@Repository
annotations implicitly include@Component
.
@Controller Annotation:
If we create a class and annotate it with @Controller
, that class will act as a controller. Internally, the @Controller
annotation includes the @Component
annotation, so the class will also be treated as a component by the Spring container.
@RestController:
If we are creating a REST API, we should annotate the class with @RestController
instead of @Controller
. A @RestController
always returns a ResponseBody
, not a JSP page (because JSP is considered an older technology).
Difference between @Controller and @RestController:
|
|
Used to handle web Requests and returns views (jsp, thymleaf) | Used to handle Restful webservices and returns data (Json, xml..) |
it does not contains @ResponseBody, you have to define explicitly if you want to send JSON/XML data instead of views. | it contains @Controller, @ResponseBody → This simplifies your code by eliminating the need to add |
In summary, use @Controller
for traditional web applications with views, and @RestController
for RESTful services that return data directly.
Properties File:
Note: By default, Spring Boot provides all the necessary configurations. However, if we want to modify any of these configurations, we can do so using the properties file (which is part of our project).
Example:
By default, Spring Boot configures the application to run on a Tomcat server with port 8080
. If we want to change this port number to 8082
, we can use the properties file as shown below:
propertiesCopy codeserver.port=8082
After updating the properties file, start the application and go to your browser. Type http://localhost:8082/
to check the output.
Memorizing all the properties for changing configurations can be challenging. To simplify this, Spring Boot provides documentation that clearly lists all available properties and their purposes. By referring to the official Spring Boot documentation, you can easily find and use the required configurations.
This makes customizing Spring Boot applications flexible and developer-friendly.
Spring Boot DevTools:
When creating an application, restarting the server manually after every change can be time-consuming and inconvenient. To address this, Spring Boot provides a dependency called Spring Boot DevTools. By using this dependency, the server will restart automatically whenever changes are made to the code, eliminating the need for manual restarts.
Benefit:
With Spring Boot DevTools, you don’t need to restart the server manually after every code change. The server restarts automatically, speeding up development and making the process more efficient.
ORM(Object-Relational Mapping) Theory:
Object-Relational Mapping is a technique that converts objects in a programming language into a table format to store them in a database. Similarly, it can also convert database tables into objects.
This(Converting) can be achieved through ORM, but how? That’s where JPA (Java Persistence API) comes into the picture.
To implement JPA, we can use frameworks like Hibernate
Spring Hibernate Configuration:
If we want to connect Spring with ORM, we need to add the Spring ORM configuration.
If we are using Hibernate, we must handle transactions (such as committing transactions, etc.), so we also need to add the Spring TX dependency.
If we use JpaRepository
, all the methods will be available by default, and we can use them directly.
Similarly, we can achieve the same functionality through the Hibernate Session
. The Session
provides various methods like save()
and createQuery()
, which we can use as needed.
The @Transactional
annotation can be used to commit the transaction.
Spring Data JPA:
findAll()
– Used to retrieve all the data from the database.getOne(aid)
– Used to get details by ID.save(a)
– Used to save data into the database.
Query DSL:
Query DSL stands for Domain-Specific Language. This allows us to achieve the following:
In a JPA repository, the default methods (like findAll()
, getOne()
, etc.) are available by default. If we want to use a method that is not present in the JPA repository, we simply need to declare the method, and we don’t need to provide an implementation. By default, JPA will handle the implementation and return the results.
For example: findByAName()
.
Here’s the corrected and improved version of your text:
@Query Annotation:
We can achieve findByAName
using the JPA repository, but what if we want to define the query manually?
@Query("from Alien where aname = :name")
List<Alien> find(@Param("name") String aname);
In this example, :name
represents a placeholder. To set a value for this placeholder, we use the @Param
annotation and specify the parameter name. This allows Spring to map the aname
variable to the name
placeholder in the query.
Here’s the corrected version of your text:
RESTful Web Services/API:
Previously, we were also receiving responses from the server. However, with REST APIs, the data is returned in a representational state. REST APIs are stateless, meaning that the server does not maintain any session state between requests.
In REST APIs, we can use all standard HTTP methods. These methods are as follows:
POST: To save data.
PUT: To update data.
GET: To retrieve data.
DELETE: To delete data.
Rest APIs are stateless:
mean that the server does not store any information about previous requests from the client. Every request is independent, and the server treats each one as a new, separate request. This means that:
The server does not remember who you are or what you did in previous requests.
Every request must contain all the information the server needs to understand and process it (like authentication, user data, etc.).
REST APIs, the data is returned in a representational state:
Means that data is not directly sent as raw data; instead, it's sent in a representational format, such as JSON or XML from server to client.
@ResponseBody:
This annotation is used to send the response data in JSON format (or XML, depending on the configuration)
@Autowired:
This annotation is used for dependency injection. It automatically injects the required dependencies into your class, removing the need for you to manually create objects\
@GetMapping(""):
This is used to map HTTP GET requests to a specific method. The value inside the parentheses (e.g., ""
) represents the path or endpoint that you want to map the method to.
For example, @GetMapping("/users")
would handle GET requests to /users
.
Java Object to JSON Conversion:
In Spring Boot, Jackson is a default library that automatically converts Java objects into JSON and vice versa. When you return a Java object in a controller method with @ResponseBody
, Jackson automatically converts it into JSON format before sending it as a response.
Optional:
In the above code, findById
returns an Optional
value. To handle cases where the data is not present in the database, we use the orElse
method.
The
orElse
method provides a default value when no data is found in the database.For example, if no record exists for the given ID, the returned object will have default values (
0
foraid
andnull
foraname
).This means it will not return an empty response but instead a default object.
Jackson JSON and Jackson XML:
Jackson JSON → Automatically Convert Java Object into JSON in Rest API Response
Jackson XML → Automatically converts Java Objects into XML in Rest API Response. and configure our Application to support XML responses.
Produces Attribute:
Using the produces
attribute, we can control and restrict the response format based on your requirements.
By default, when using @GetMapping
, the response is returned in JSON format. If the Jackson XML dependency is added, the response can also be returned in XML format by specifying the Accept
header in tools like Postman (e.g., Accept: application/xml
) as below.
However, if we want to restrict the response format to only XML and disallow JSON, we can use the produces
attribute in the @GetMapping
annotation.
The produces Attribute controls which format is returned to user.
@RestController
public class UserController {
@GetMapping(value = "/users", produces = "application/xml")
public User getUser() {
return new User(1L, "John Doe"); // The response will be returned in XML format only
}
}
Even if the client sends a request with Accept: application/json
(From postman), the server will respond with a 406 Not Acceptable status code because JSON is not allowed for this endpoint.
If you want to allow both JSON and XML formats, you can include multiple values in the produces
attribute:
@GetMapping(value = "/users", produces = {"application/json", "application/xml"})
This allows the client to specify their preferred format using the Accept
header in postman.
Consumes Attribute:
The consumes
attribute specifies the type of data the server can accept from the client.
For example, if you specify consumes = "application/json"
, the server will accept only JSON data. If the client sends XML data, it will result in an error.
Example with @PostMapping
:
@PostMapping(value = "/users", consumes = "application/json")
public void saveUser(@RequestBody User user) {
userService.save(user); // Only accepts JSON data
}
Produces → Restrict the response format based on our Requirement → used with @GetMapping
Consumes → Restrict the input format that server accepts from client. → used with @PostMapping
Request Body and Response Body:
@RequestBody:
@RequestBody
automatically converts incoming JSON or XML data into a Java object before processing and saving into database.
To handle this conversion, we use the @RequestBody
annotation in a @PostMapping
method.
Example:
@PostMapping("/users")
public void saveUser(@RequestBody User user) {
userService.save(user); // JSON/XML data is converted to a User object
}
@ResponseBody:
while converting Java object into Json/XML for client, we can use response body along with GetMapping Annotation.
Example:
@GetMapping("/users/{id}")
public User getUser(@PathVariable Long id) {
return userService.getUserById(id); // The User object is converted to JSON/XML
}
In the code, we have not explicitly used the @ResponseBody
annotation anywhere. This is because the class is annotated with @RestController
. The @RestController
annotation combines both @Controller
and @ResponseBody
. As a result, @ResponseBody
is automatically applied to all methods within the class.
Components in Spring Boot:
@Entity
:This annotation makes a class an entity, meaning it will map to a database table.
The class name will act as the table name
@Id
:Marks a field in the class as the primary key for the database table.
@GeneratedValue(strategy =
GenerationType.AUTO
):Using
GenerationType.AUTO
, the database will automatically generate the primary key.@Service
:Used to mark a class as a service. Services contain business logic and interact with repositories.
@Repository
:Marks a class as a repository. Repositories are used to interact with the database.
Important Points:
Note: In Spring Boot, we don’t need to manually create database tables. With JPA (Java Persistence API), tables are created automatically based on the entity classes.
This behavior is managed by the spring.jpa.hibernate.ddl-auto
property in the application.properties
file:
create
: Creates tables every time the application runs.update
: Updates the schema without deleting data.validate
: Verifies the schema matches the entities.none
: No schema management.
Exception Handling in Spring Boot:
@ControllerAdvice
:
If you want a specific class to handle all exceptions in your entire Spring Boot application, you annotate that class with @ControllerAdvice
. This annotation makes the class a global exception.
Any exceptions thrown by controllers or other parts of the application can be caught and handled by this class.
@ResponseStatus
:
Used to specify the HTTP status code that should be returned after handling an exception.
For example, if a resource is not found, you can return a 404 NOT FOUND
status.
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(DepartmentNotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND) // Specifies the HTTP status code
public ResponseEntity<ErrorMessage> handleDepartmentNotFoundException(
DepartmentNotFoundException ex, WebRequest request) {
// Create a custom error message
ErrorMessage errorMessage = new ErrorMessage(
HttpStatus.NOT_FOUND.value(), // Status code
ex.getMessage() // Error message from exception
);
// Wrap the error message in ResponseEntity and return
return new ResponseEntity<>(errorMessage, HttpStatus.NOT_FOUND);
}
}
Handling Specific Exceptions:
To handle a particular exception, create a method in the @ControllerAdvice
-annotated class and annotate it with @ExceptionHandler
as in above.
Example:
If we have a custom exception DepartmentNotFoundException
, we can create a method to handle it.
Custom Error Format:
To provide a structured response (e.g., a status code and a message), create a separate class, such as ErrorMessage
.
This class can have fields like status
and message
to store details about the error.
To send the ErrorMessage
object as a response, wrap it in a ResponseEntity
. This ensures the response includes both the body and the appropriate HTTP status.
You can create separate methods to handle different exceptions
A single method can handle multiple exceptions by specifying them in the @ExceptionHandler
annotation:
@ExceptionHandler({Exception1.class, Exception2.class})
public ResponseEntity<ErrorMessage> handleExceptions(Exception ex) {
// Handle both exceptions here
}
Using WebRequest
:
WebRequest
can be used to get details like the request URI or headers, which can be helpful for debugging or logging.
And the main advantage in spring boot is if we want to change the database from postgre to MySQL. we don’t need to modify everything, just we need to add configurations(MYSQL) in properties file and we need to add mysql driver dependency in pom.xml file.
Subscribe to my newsletter
Read articles from LakshmiPerumal directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by