0% found this document useful (0 votes)
43 views182 pages

Spring, SpringBoot, JPA, Security, Hibernate - Zero To Master

This e-book serves as a comprehensive guide to mastering the Spring ecosystem, covering foundational principles of the Spring Framework, Spring Boot, JPA, and Hibernate, along with practical skills for deploying enterprise applications. It includes detailed chapters on various topics such as Spring Core, web applications, security, data persistence, and REST APIs. The guide aims to equip readers with the knowledge and skills needed to develop robust applications in Java.

Uploaded by

nikhilmore9767
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
43 views182 pages

Spring, SpringBoot, JPA, Security, Hibernate - Zero To Master

This e-book serves as a comprehensive guide to mastering the Spring ecosystem, covering foundational principles of the Spring Framework, Spring Boot, JPA, and Hibernate, along with practical skills for deploying enterprise applications. It includes detailed chapters on various topics such as Spring Core, web applications, security, data persistence, and REST APIs. The guide aims to equip readers with the knowledge and skills needed to develop robust applications in Java.

Uploaded by

nikhilmore9767
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 182

Spring, SpringBoot, JPA, Hibernate: Zero To

Master
Welcome to your comprehensive guide to mastering the Spring ecosystem. This e-book is designed
to serve as a detailed, in-depth companion to your learning journey, taking you from the foundational
principles of the Spring Framework to the rapid development power of Spring Boot, the intricacies of
data persistence with JPA and Hibernate, and finally, the practical skills needed to deploy
enterprise-grade applications to the cloud.

We will leave no stone unturned. Every concept, every line of code, and every best practice
presented in the course is detailed here for your reference. Let's begin our journey from Zero to
Master.
Table of Contents

Sequential Index
Part 1: Foundations of Spring
●​ Chapter 1: Introduction to the Spring Framework
○​ 1.1 What is Spring?
○​ 1.2 Behind the Scenes of a Web App
○​ 1.3 Why Should We Use Frameworks?
○​ 1.4 A Tale of Two Titans: Spring vs. Java EE
■​ 1.4.1 Java EE Release Timeline
■​ 1.4.2 Spring Release Timeline
○​
○​ 1.5 The Spring Ecosystem: A World of Projects
●​ Chapter 2: Spring Core: The Heart of the Framework
○​ 2.1 Core Principles: Inversion of Control (IoC) & Dependency Injection (DI)
○​ 2.2 Advantages of IoC & DI
○​ 2.3 Spring Beans, Context, and SpEL
○​ 2.4 The Spring IoC Container
○​ 2.5 A Note on Maven
●​ Chapter 3: Managing Beans in Spring
○​ 3.1 Adding New Beans to Spring Context: @Bean Annotation
○​ 3.2 The NoUniqueBeanDefinitionException
○​ 3.3 Solving NoUniqueBeanDefinitionException (by Name, Custom Name,
@Primary)
○​ 3.4 Adding Beans with @Component Annotation
○​ 3.5 Spring Stereotype Annotations (@Service, @Repository, @Controller)
○​ 3.6 @Bean Vs @Component
○​ 3.7 Bean Lifecycle Hooks: @PostConstruct & @PreDestroy
○​ 3.8 Adding New Beans Programmatically
○​ 3.9 Adding New Beans Using XML Configs
●​ Chapter 4: Wiring and Autowiring: Connecting the Dots
○​ 4.1 Introduction to Beans Wiring inside Spring
○​ 4.2 No Wiring Scenario Inside Spring
○​ 4.3 Wiring Beans Using a Method Call
○​ 4.4 Wiring Beans Using Method Parameters
●​ Chapter 5: Autowiring Beans in Depth
○​ 5.1 Inject Beans using @Autowired on Class Fields
○​ 5.2 Inject Beans using @Autowired on Setter Method
○​ 5.3 Inject Beans using @Autowired with Constructor (Best Practice)
○​ 5.4 How Autowiring Works with Multiple Beans of the Same Type (by Name,
@Primary, @Qualifier)
○​ 5.5 Understanding & Avoiding Circular Dependencies
○​ 5.6 Assignment Related to Beans, Autowiring, DI
●​ Chapter 6: Understanding Bean Scopes
○​ 6.1 Singleton Bean Scope
○​ 6.2 The Problem of State in Singleton Beans: Race Conditions
○​ 6.3 Use Cases of Singleton Beans (And How to Use Them Safely)
○​ 6.4 Eager & Lazy Instantiation
○​ 6.5 Prototype Bean Scope
○​ 6.6 Singleton Vs Prototype
●​ Chapter 7: Aspect-Oriented Programming (AOP)
○​ 7.1 Introduction to AOP
○​ 7.2 AOP in Action: Cleaning Up Business Logic
○​ 7.3 AOP Jargons: The 3 Ws
○​ 7.4 Weaving inside AOP
○​ 7.5 Advice Types Inside AOP
○​ 7.6 Configuring Advices inside AOP (Execution Expression, Annotations)

Part 2: Building Web Applications with Spring Boot


●​ Chapter 8: Building Web Apps with Spring MVC
○​ 8.1 Overview of a Web App
○​ 8.2 Role of Servlets Inside Web Apps
○​ 8.3 Evolution of Web Apps inside the Java Ecosystem
○​ 8.4 Developing Web Applications Using Spring
●​ Chapter 9: Spring Boot: The Hero of the Spring Framework
○​ 9.1 Introduction to Spring Boot
○​ 9.2 Before & After SpringBoot
○​ 9.3 The Magic of SpringBoot: Important Features (Starters, Autoconfiguration)
○​ 9.4 Getting Started with SpringBoot: Quick Recap
○​ 9.5 Quick Tip: Multiple Paths
●​ Chapter 10: Dynamic Views with Thymeleaf & DevTools
○​ 10.1 Introduction to Thymeleaf
○​ 10.2 SpringBoot DevTools
●​ Chapter 11: Deeper into Spring MVC and Validation
○​ 11.1 Introduction to MVC Pattern
○​ 11.2 Spring MVC Architecture & Internal Flow
○​ 11.3 Quick Tip: View Controllers
○​ 11.4 Reducing Boilerplate Code with Lombok
○​ 11.5 @RequestParam Annotation
○​ 11.6 @PathVariable Annotation
○​ 11.7 Validation with Spring Boot
○​ 11.8 Important Validation Annotations
○​ 11.9 Validation in Practice
●​ Chapter 12: Spring Web Scopes
○​ 12.1 Defining Spring Web Scopes (Request, Session, Application)
○​ 12.2 Key Points of Spring Web Scopes

Part 3: Security
●​ Chapter 13: Securing Your Application with Spring Security
○​ 13.1 Introduction to Spring Security
○​ 13.2 Authentication & Authorization
○​ 13.3 Quick Tip: Default Security
○​ 13.4 Default Security Configurations in Spring Security
○​ 13.5 Configure permitAll() with Spring Security
○​ 13.6 Configure denyAll() with Spring Security
○​ 13.7 Configure Custom Security Configs & CSRF Disable
○​ 13.8 In-Memory Authentication in Spring Security
○​ 13.9 Configuring Login & Logout Page
●​ Chapter 14: Advanced Topics in Spring MVC & Security
○​ 14.1 Quick Tip: Thymeleaf & Spring Security Integration
○​ 14.2 @ControllerAdvice & @ExceptionHandler for Global Exception Handling
○​ 14.3 Quick Tip: Fine-Grained Exception Handling
○​ 14.4 Cross-Site Request Forgery (CSRF)
○​ 14.5 Solution to CSRF
○​ 14.6 Quick Tip: CSRF in Spring Security
Part 4: Data Persistence
●​ Chapter 15: Database Integration with Spring Boot
○​ 15.1 Spring Boot & H2 Database
●​ Chapter 16: Database Connectivity with JDBC and Spring JDBC
○​ 16.1 Intro to JDBC & Problems With It
○​ 16.2 Sample Program using JDBC
○​ 16.3 Intro to Spring JDBC
○​ 16.4 Spring JDBC - Who Does What?
○​ 16.5 Using JdbcTemplate
○​ 16.6 JdbcTemplate in Action: Query Examples
○​ 16.7 Using RowMapper
○​ 16.8 Quick Tip: BeanPropertyRowMapper
○​ 16.9 Using NamedParameterJdbcTemplate
○​ 16.10 Quick Tip: Spring Boot and JdbcTemplate
●​ Chapter 17: Simplifying Persistence with Spring Data
○​ 17.1 Intro to Spring Data
○​ 17.2 Spring Data Repository Abstraction (Repository, CrudRepository, etc.)
○​ 17.3 Spring Data Repository Hierarchy
○​ 17.4 Quick Tip: @Repository vs. Repository
○​ 17.5 Technology-Specific Repositories
○​ 17.6 Intro to Spring Data JPA
○​ 17.7 Derived Query Methods in Spring Data JPA
○​ 17.8 Quick Tip: Anatomy of a Derived Query
○​ 17.9 Supported Keywords inside Method Names
●​ Chapter 18: Advanced Spring Data JPA Features
○​ 18.1 Auditing Support By Spring Data JPA
○​ 18.2 Quick Tip: Printing SQL Queries
○​ 18.3 Spring MVC Custom Validations
●​ Chapter 19: Modeling Relationships with JPA
○​ 19.1 One to One Relationship inside JPA
○​ 19.2 JPA Fetch Types
○​ 19.3 Cascade Types
●​ Chapter 20: Advanced Spring Security Implementation
○​ 20.1 Spring Security AuthenticationProvider
○​ 20.2 How Passwords are Validated W/O PasswordEncoder
○​ 20.3 Passwords Management: Different Ways of Pwd Management (Encoding,
Encryption, Hashing)
○​ 20.4 Quick Tip: PasswordEncoder in Spring Security
○​ 20.5 How Passwords are Validated With Hashing & PasswordEncoder
○​ 20.6 Quick Tip: Disabling JPA Validation
●​ Chapter 21: Advanced Relationship Mappings
○​ 21.1 One to Many & Many to One Relationship inside JPA
○​ 21.2 Many to Many inside JPA
●​ Chapter 22: Sorting and Pagination with Spring Data JPA
○​ 22.1 Sorting with Spring Data JPA (Static vs. Dynamic)
○​ 22.2 Pagination with Spring Data JPA
○​ 22.3 Spring Data JPA Custom Queries (@Query, Named Queries)
○​ 22.4 JPQL inside Spring Data JPA
○​ 22.5 Sorting Examples
○​ 22.6 Pagination Examples
○​ 22.7 @Query Examples
○​ 22.8 @NamedQuery & @NamedNativeQuery Examples
○​ 22.9 Quick Tip: Multiple Named Queries

Part 5: REST APIs and Production Operations


●​ Chapter 23: Building and Consuming REST Services
○​ 23.1 Implementing REST Services
○​ 23.2 Implementing REST Services with Spring MVC (@ResponseBody)
○​ 23.3 Spring MVC Architecture with REST Service
○​ 23.4 The @RestController Annotation
○​ 23.5 Different Annotations & Classes in REST
○​ 23.6 Cross-Origin Resource Sharing (CORS)
○​ 23.7 Enabling CORS
○​ 23.8 Quick Tip: Controlling JSON Output with Jackson
○​ 23.9 Consuming REST Services (OpenFeign, RestTemplate, WebClient)
○​ 23.10 Consuming REST Services using OpenFeign
○​ 23.11 Consuming REST Services using RestTemplate
○​ 23.12 Consuming REST Services using WebClient
●​ Chapter 24: Hypermedia and Spring Data REST
○​ 24.1 Spring Data REST
○​ 24.2 HAL Explorer
○​ 24.3 Quick Tip: Customizing Spring Data REST
●​ Chapter 25: Production-Ready Features: Logging, Properties, and Profiles
○​ 25.1 Logging inside SpringBoot
○​ 25.2 Configuring Logging
○​ 25.3 Anatomy of a Spring Boot Log Message
○​ 25.4 Quick Tip: Lombok for Logging
●​ Chapter 26: Externalized Configurations and Profiles
○​ 26.1 Properties Externalized Configurations
○​ 26.2 Reading Properties in Code (@Value, Environment,
@ConfigurationProperties)
○​ 26.3 Reading properties with @PropertySource
○​ 26.4 Profiles for Grouping Configurations
○​ 26.5 Quick Tip: Activating a Profile
○​ 26.6 Conditional Bean Creation using Profiles
●​ Chapter 27: Monitoring with Spring Boot Actuator
○​ 27.1 Introduction to Spring Boot Actuator
○​ 27.2 API Paths Provided by Actuator
●​ Chapter 28: Deploying Spring Boot Applications to the Cloud (AWS)
○​ 28.1 Deploying Spring Boot Applications
○​ 28.2 AWS EC2 vs AWS Elastic Beanstalk
○​ 28.3 AWS Elastic Beanstalk - How it works
●​ Conclusion: THANK YOU
Chapter 1: Introduction to the Spring Framework
What is Spring?
The Spring Framework (or simply, Spring) is a mature, powerful, and highly flexible framework
focused on building web applications in Java. It has become the de-facto standard in the Java world
due to its focus on speed, simplicity, and productivity, making the development process quicker,
easier, and safer for everyone.

●​ Versatility: Whether you're building secure, reactive, cloud-based microservices for the web,
or complex streaming data flows for the enterprise, Spring has the tools to help.
●​ Origins: Born as an alternative to Enterprise JavaBeans (EJBs) in the early 2000s, the
Spring framework quickly overtook its opponent with its simplicity, variety of features, and its
seamless third-party library integrations.
●​ Market Dominance: It is so popular that its main competitor, Java EE, struggled to keep
pace. When Oracle stopped the evolution of Java EE 8, the community took over its
maintenance via the Eclipse Foundation, renaming it Jakarta EE.
●​ Continuous Innovation: The main reason for Spring framework's success is its commitment
to innovation. It regularly introduces new features and projects based on the latest market
trends and the needs of the developer community. A prime example of this is SpringBoot.
●​ Open Source: Spring is open source and boasts a large and active community that provides
continuous feedback based on a diverse range of real-world use cases.

Behind the Scenes of a Web App


When a user interacts with a web application, they only see the tip of the iceberg: the User Interface
(UI/UX), the business logic it exposes, and its responsiveness.

However, beneath the surface lies a massive foundation of supporting components that are
essential for any non-trivial application to function correctly. These include:

●​ Sessions & Caching: Managing user state and storing frequently accessed data to improve
performance.
●​ Transactions: Ensuring that a series of database operations either all succeed or all fail
together, maintaining data integrity.
●​ Security: Protecting the application from threats, authenticating users, and authorizing their
access to resources.
●​ DB Persistence: The logic for storing and retrieving data from a database.
●​ Logging: Recording application events for debugging and monitoring.
●​ Data Transfer: Moving data between different layers of the application or between different
systems.
●​ Batch Processing: Handling large volumes of data offline.

A framework like Spring provides robust, pre-built solutions for all these "underwater" components,
allowing developers to focus on the visible part of the iceberg.

Why Should We Use Frameworks?


To understand the value of a framework, consider two analogies.

Analogy 1: The Chefs

●​ Chef Sanjeev (No Framework): He decides to prepare a Pizza by making everything


himself from raw ingredients. He mills the flour, cultures the cheese, grows the tomatoes for
the sauce, and builds his own oven. His pizza preparation time is very long, he cannot easily
scale to handle more orders, the taste might be inconsistent, and he spends most of his
energy on raw materials instead of the final preparation. This leads to more effort for less
revenue.
●​ Chef Vicky (Uses a Framework): He uses the best readily available ingredients like
high-quality Cheese, pre-made Pizza Dough, etc. to prepare his Pizza. His preparation time
is less, he can easily scale his restaurant orders, he gets a consistent taste, and he can
focus his energy on the pizza preparation itself. This leads to less effort and more
results/revenue.

Analogy 2: The Developers

●​ Dev Vicky (No Framework): He builds his own code by himself to build a web app. He
needs to write code for Security, Logging, etc., from scratch. Scaling his application is not an
option until he tests everything thoroughly. The app may not work in a predictable manner,
and he has to focus more on the supporting components than the business logic. This results
in more effort and less revenue.
●​ Dev Sanjeev (Uses a Framework): He uses the best readily available frameworks like
Spring, Angular, etc., to build a web app. He can leverage Security, Logging, etc., directly
from these frameworks. He can easily scale his application. The app will work in a
predictable manner because it's built on a tested foundation. He can focus more on the
business logic. This leads to less effort and more results/revenue.
A Tale of Two Titans: Spring vs. Java EE
The history of Java enterprise development is defined by the evolution of Java EE and Spring.

Java EE Release Timeline

Java EE (originally J2EE) was the official specification for building enterprise applications. Its
timeline shows a steady but sometimes slow evolution:

●​ J2EE 1.2 (1999)


●​ J2EE 1.3 (2001)
●​ J2EE 1.4 (2003)
●​ JAVA EE 5 (2006)
●​ JAVA EE 6 (2009)
●​ JAVA EE 7 (2013)
●​ JAVA EE 8 (2017)
●​ Jakarta EE 8 (2019)
●​ Jakarta EE 9 (2020)
●​ Jakarta EE 9.1 (2021)
●​ Jakarta EE 10 (2022)

Key Points about Java/Jakarta EE:

●​ Java/Jakarta Enterprise Edition (EE) contains specifications like Servlets, JSPs, EJB, JMS,
RMI, JPA, JSF, JAXB, JAX-WS, Web Sockets etc.
●​ Components of Java/Jakarta Enterprise Edition (EE) like EJB and Servlets were complex in
nature, which is why everyone adapted the Spring framework for web applications
development.
●​ Java EE quit the race against the Spring framework when Oracle stopped the evolution of
Java EE 8, and the community took over its maintenance via Jakarta EE.
●​ Since Oracle owns the trademark for the name "Java", Java EE was renamed to Jakarta EE.
All the packages were updated with javax.* to jakarta.* namespace change.
Spring Release Timeline

Spring was created as a direct response to the complexity of early J2EE. Its timeline reflects a more
rapid pace of innovation.

●​ 1.0 (2004)
●​ 2.0 (2006)
●​ 3.0 (2009)
●​ 4.0 (2013)
●​ 5.0 (2017)
●​ 6.0 (2022)

Key Points about Spring's History:

●​ The first version of Spring was written by Rod Johnson, who released the framework with
the publication of his book Expert One-on-One J2EE Design and Development in October
2002.
●​ Spring came into being in 2003 as a response to the complexity of the early J2EE
specifications. While some consider Java EE and Spring to be in competition, Spring is, in
fact, complementary to Java EE. The Spring programming model does not embrace the Java
EE platform specification; rather, it integrates with carefully selected individual specifications
from the EE umbrella.
●​ Spring continues to innovate and evolve. Beyond the Spring Framework, there are other
projects, such as Spring Boot, Spring Security, Spring Data, Spring Cloud, Spring
Batch, among others.
The Spring Ecosystem: A World of Projects
Spring is not a single monolith. It is a family of projects that work together to provide a
comprehensive development platform. A typical modern web application sits at the center,
leveraging various Spring projects for different needs.

●​ Spring Core & Spring MVC: The foundation for dependency injection and web capabilities.
●​ Spring Boot: For rapid application development and simplified configuration.
●​ Spring Data: For simplified data access to various databases.
●​ Spring Security: For authentication and authorization.
●​ Spring Cloud: For building distributed systems and microservices.
●​ Spring Session: For managing user session information.
●​ Spring Integration: For enterprise application integration patterns.
●​ Spring AMQP: For messaging with RabbitMQ.

The evolution of application development over the years shows a clear trend towards more
abstraction and separation of concerns, a trend that the Spring Framework has consistently led and
adapted to.
Chapter 2: Spring Core: The Heart of the Framework
At the very center of the Spring ecosystem is the Spring Core module. It is the heart of the entire
Spring. It contains the base framework classes, principles, and mechanisms upon which everything
else is built.

●​ The entire Spring Framework and other projects of Spring are developed on top of the Spring
Core.
●​ Spring Core contains the following important components:
○​ IoC (Inversion of Control)
○​ DI (Dependency Injection)
○​ Beans
○​ Context
○​ SpEL (Spring Expression Language)
○​ IoC Container

Core Principles: Inversion of Control (IoC) & Dependency


Injection (DI)
These two concepts are the fundamental pillars that give Spring its power and flexibility. They are
often used interchangeably, but there is a key distinction.

●​ Inversion of Control (IoC): This is a high-level Software Design Principle, independent of


any language. It does not actually create the objects but describes the way in which objects
should be created. In traditional programming, your code is in control: it creates objects, calls
methods, and dictates the flow. With IoC, this control is inverted. The framework or service
takes control of the program flow and is responsible for creating objects and calling your
code when needed. This is often called the "Hollywood Principle": "Don't call us, we'll call
you."
●​ Dependency Injection (DI): This is the design pattern through which Inversion of Control is
achieved. An object's "dependencies" (other objects it needs to function) are "injected" into it
by the framework. Through Dependency Injection, the responsibility of creating objects is
shifted from the application to the Spring IoC container. It reduces coupling between multiple
objects as they are dynamically injected by the framework.
Illustrative Example:

Consider a ReportService that needs to send an email.

Without DI (Tight Coupling):

Generated java

public class ReportService {


private SmtpEmailSender emailSender = new SmtpEmailSender(); // Creates its own dependency

public void generateAndSendReport() {


// ... logic to generate report ...
emailSender.send("[email protected]", "Your Report", "...");
}
}

Here, ReportService is tightly coupled to SmtpEmailSender. If you want to switch to a different


sender (e.g., RestApiEmailSender) or want to test this class without sending a real email, you have
to change the code of ReportService.

With DI (Loose Coupling):

Generated java

public interface EmailSender {


void send(String to, String subject, String body);
}

public class ReportService {


private final EmailSender emailSender; // Depends on an interface, not a concrete class

// The dependency is "injected" through the constructor


public ReportService(EmailSender emailSender) {
this.emailSender = emailSender;
}
public void generateAndSendReport() {
// ... logic to generate report ...
emailSender.send("[email protected]", "Your Report", "...");
}
}

Now, ReportService is loosely coupled. It doesn't know or care about the specific implementation
of EmailSender. The Spring framework can create an instance of SmtpEmailSender or a
MockEmailSender for testing and "inject" it into the ReportService at runtime.

Advantages of IoC & DI


Using IoC and DI provides significant benefits:

●​ Loose coupling between the components: As seen in the example, classes are not tied to
specific implementations, making the system more flexible.
●​ Minimizes the amount of code in your application: You don't need to write factory classes
or object creation logic.
●​ Makes unit testing easy with different mocks: You can easily inject mock implementations
of dependencies during tests.
●​ Increased system maintainability & module reusability: Components can be swapped or
updated with minimal impact on other parts of the system.
●​ Allows concurrent or independent development: Different teams can work on different
components as long as they agree on the interfaces.
●​ Replacing modules has no side effect on other modules: As long as the new module
respects the contract (interface), the rest of the application remains unaffected.

Spring Beans, Context, and SpEL


These are the core vocabulary of a Spring developer.

●​ Spring Bean: Any normal Java class that is instantiated, assembled, and otherwise
managed by a Spring IoC container is called a Spring Bean. Essentially, it's a POJO (Plain
Old Java Object) that you hand over to Spring for management.
●​ Configuration Metadata: Beans are created with the configuration metadata that you supply
to the container, either in the form of XML configs (the old way) or Annotations (the modern
way).
●​ Lifecycle Management: The Spring IoC Container manages the lifecycle of a Spring Bean,
including its scope and injecting any required dependencies into the bean.
●​ Context: The context is like a memory location of your app in which we add all the object
instances that we want the framework to manage. By default, Spring doesn't know any of the
objects you define in your application. To enable Spring to see your objects, you need to add
them to the context.
●​ SpEL (Spring Expression Language): SpEL provides a powerful expression language for
querying and manipulating an object graph at runtime, including setting and getting property
values, property assignment, and method invocation.

The Spring IoC Container


The IoC container is the workhorse of the framework.

Responsibilities:

●​ To instantiate the application class


●​ To configure the object
●​ To assemble the dependencies between the objects

Types of IoC Containers:

●​ org.springframework.beans.factory.BeanFactory: The basic container.


●​ org.springframework.context.ApplicationContext: The advanced container, which is
a superset of BeanFactory. You will almost always use this one.

The Spring container uses dependency injection (DI) to manage the components/objects that make
up an application.

The flow is simple: you provide Configuration Instructions and your Application Classes
(POJOs) to the Spring Application Context. The container works its magic and creates a fully
configured app, ready for use.

A Note on Maven
MAVEN is a powerful build automation and dependency management tool. While not part of Spring
itself, it is essential for managing modern Spring projects.

●​ Dependency Management: Spring applications are built from many different libraries
(dependencies). Instead of manually ing JAR files, you declare the dependencies you need
in a pom.xml file. Maven then automatically s the correct versions of these libraries and their
sub-dependencies for you.
●​ Build Lifecycle: Maven provides a standard lifecycle for building projects, including
compiling code, running tests, packaging the application (into a JAR or WAR file), and
deploying it.

A typical Spring dependency in pom.xml looks like this:

Generated xml

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>

Xml
Chapter 3: Managing Beans in Spring
Now we'll explore the practical ways to add beans to the Spring Context.

Adding New Beans to Spring Context: @Bean Annotation


There are two primary ways an object can exist in your application: one you create, and one Spring
creates.

●​ Left Side (Manual Creation):

Generated java

Vehicle vehicle = new Vehicle();

Java

●​ When we create a Java object with the new () operator directly as shown above, then your
Spring Context/Spring IoC Container will not have any clue about the object. It exists
outside of Spring's management.
●​ Right Side (Spring Managed):
●​ Generated java

@Configuration
public class ProjectConfig {
@Bean
Vehicle vehicle() {
var veh = new Vehicle();
veh.setName("Audi A8");
return veh;
}
}

●​ The @Bean annotation lets Spring know that it needs to call this method when it initializes its
context. Spring will take the returned object/value, in this case, a Vehicle instance, and add
it to the Spring context/Spring IoC Container. This object is now a managed bean.

The NoUniqueBeanDefinitionException
A common problem arises when the context has multiple beans of the same type.

When we create multiple objects of the same type and try to fetch the bean from the context by type,
then Spring cannot guess which instance you've declared you want to refer to. This will lead to a
NoUniqueBeanDefinitionException.

Code Causing the Exception:

Generated java

// Configuration class with multiple Vehicle beans


@Configuration
public class ProjectConfig {
@Bean
Vehicle vehicle1() {
var veh = new Vehicle(); veh.setName("Audi"); return veh;
}
@Bean
Vehicle vehicle2() {
var veh = new Vehicle(); veh.setName("Honda"); return veh;
}
@Bean
Vehicle vehicle3() {
var veh = new Vehicle(); veh.setName("Ferrari"); return veh;
}
}

// Main application logic


var context = new AnnotationConfigApplicationContext(ProjectConfig.class);
// This line will throw the exception!
Vehicle veh = context.getBean(Vehicle.class);
Java

Console Output:

Generated code

Exception in thread "main" org.springframework.beans.factory.NoUniqueBeanDefinitionException: No


qualifying bean of type 'com.example.beans.Vehicle' available: expected single matching bean but found
3: vehicle1,vehicle2,vehicle3

Solving NoUniqueBeanDefinitionException
To avoid this exception, you must be more specific when asking for a bean.

Solution 1: Fetch by Name

By default, the name of the bean is the same as the name of the method that created it. You can
fetch the bean by providing its name.

Generated java

var context = new AnnotationConfigApplicationContext(ProjectConfig.class);


// Fetch the bean named "vehicle1"
Vehicle veh = context.getBean("vehicle1", Vehicle.class);
System.out.println("Vehicle name from Spring Context is: " + veh.getName());

Java

Output on Console:

Generated code

Vehicle name from Spring Context is: Audi


Solution 2: Custom Bean Names

You can provide custom names to your beans using the name or value attribute of the @Bean
annotation.

Generated java

@Configuration
public class ProjectConfig {
@Bean(name = "audiVehicle")
Vehicle vehicle1() { /* ... */ }

@Bean(value = "hondaVehicle")
Vehicle vehicle2() { /* ... */ }

@Bean("ferrariVehicle") // "name" is the default attribute


Vehicle vehicle3() { /* ... */ }
}

// Fetching the custom-named beans


Vehicle veh1 = context.getBean("audiVehicle", Vehicle.class);
Vehicle veh2 = context.getBean("hondaVehicle", Vehicle.class);
Vehicle veh3 = context.getBean("ferrariVehicle", Vehicle.class);

System.out.println("Vehicle name from Spring Context is: " + veh1.getName()); // Audi


System.out.println("Vehicle name from Spring Context is: " + veh2.getName()); // Honda
System.out.println("Vehicle name from Spring Context is: " + veh3.getName()); // Ferrari

Java

Solution 3: Using

When you have multiple beans of the same kind inside the Spring context, you can make one of
them primary by using the @Primary annotation. The primary bean is the one which Spring will
choose if it has multiple options and you don't specify a name. In other words, it is the default bean
that Spring Context will consider in case of confusion due to multiple beans present of the same
type.

Generated java

@Configuration
public class ProjectConfig {
@Bean(name="audiVehicle")
Vehicle vehicle1() { /* ... returns Audi */ }

@Bean(value="hondaVehicle")
Vehicle vehicle2() { /* ... returns Honda */ }

@Bean("ferrariVehicle")
@Primary // Mark this bean as the default
Vehicle vehicle3() { /* ... returns Ferrari */ }
}

// Fetching by type now works and returns the primary bean


var context = new AnnotationConfigApplicationContext(ProjectConfig.class);
Vehicle primaryVehicle = context.getBean(Vehicle.class);
System.out.println("Primary Vehicle name from Spring Context is: " + primaryVehicle.getName());

Java

Output on Console:

Generated code

Primary Vehicle name from Spring Context is: Ferrari

Adding Beans with @Component Annotation


@Component is one of the most commonly used stereotype annotations by developers. Using this,
we can easily create and add a bean to the Spring context by writing less code compared to the
@Bean option. With stereotype annotations, we need to add the annotation above the class for
which we need to have an instance in the Spring context.

To make this work, we must also use the @ComponentScan annotation over the configuration class,
to instruct Spring on where to find the classes marked with stereotype annotations.

Example Implementation:

Vehicle Class:

Generated java

@Component // Mark this class as a Spring component


public class Vehicle {
private String name;
// Getters, Setters, etc.
public void printHello() {
System.out.println("Printing Hello from Component Vehicle Bean");
}
}J

Configuration Class:

Generated java

@Configuration
@ComponentScan(basePackages = "com.example.beans") // Tell Spring to scan this package
public class ProjectConfig {
}

Java

Main Application:

Generated java

var context = new AnnotationConfigApplicationContext(ProjectConfig.class);


Vehicle vehicle = context.getBean(Vehicle.class);
System.out.println("Component Vehicle name from Spring Context is: " + vehicle.getName());
vehicle.printHello();

Java

Output on Console:

Generated code

Component Vehicle name from Spring Context is: null


Printing Hello from Component Vehicle Bean
(The name is null because we haven't set it yet. We'll see how to do that shortly.)

Spring Stereotype Annotations


Spring provides special annotations called Stereotype annotations which will help to create the
Spring beans automatically in the application context. The main stereotype annotations in Spring are
@Component, @Service, @Repository, and @Controller.

●​ @Component: This is the generic, all-purpose stereotype annotation. It can be used on top of
any Java class. It is the base for other annotations.
●​ @Service: This can be used on top of the classes inside the service layer, especially where
we write business logic and make external API calls.
●​ @Repository: This can be used on top of the classes which handle the code related to
Database access related operations like Insert, Update, Delete, etc.
●​ @Controller: This can be used on top of the classes inside the Controller layer of MVC
applications.

Functionally, they all behave the same way: they register the class as a bean. The difference is
semantic; they help developers and tools understand the role of the class in the application
architecture.

@Bean Vs @Component
This is a critical distinction for any Spring developer.

Feature @Bean @Component

Instances One or more instances of the Only one instance of the class can
class can be added to the Spring be added to the Spring context.
Context.

Source We can create an object instance We can create an object instance for
Class of any type of class including application classes only which are
present inside libraries like String created by the Dev team.
etc.
Code Usually we need to write more Bean instances can be created with
Verbosity code like separate methods to very less code like using
create beans instances. @Component on top of the class.

Control Developer will have full control in Developer will not have any control
creating and configuring the in creating and configuring the bean.
bean.

Creation Spring framework creates the Spring framework takes charge of


Logic bean based on the instructions creating the bean and post that the
and values provided by the Developer will have access to it.
Developer.

Bean Lifecycle Hooks: @PostConstruct & @PreDestroy


We have seen that when we are using stereotype annotations, we don't have control while creating a
bean. But what if we want to execute some instructions post Spring creates the bean? For the same,
we can use the @PostConstruct annotation.

We can define a method in the component class and annotate that method with @PostConstruct,
which instructs Spring to execute that method after it finishes creating the bean.

Similarly, the @PreDestroy annotation can be used on top of the methods and Spring will make sure
to call this method just before clearing and destroying the context. This can be used in scenarios
where we want to close any IO resources, Database connections, etc.

Spring borrows both @PostConstruct and @PreDestroy annotations from Java EE


(jakarta.annotation package).

Example:

Generated java

@Component
public class Vehicle {

private String name;


// getter for name

@PostConstruct
public void initialize() {
this.name = "Honda";
}

@PreDestroy
public void destroy() {
System.out.println("Destroying Vehicle Bean");
}
}

// Main method
var context = new AnnotationConfigApplicationContext(ProjectConfig.class);
Vehicle vehicle = context.getBean(Vehicle.class);
System.out.println("Component Vehicle name from Spring Context is: " + vehicle.getName());
context.close(); // This triggers the @PreDestroy method

Java

Output on Console:

Generated code

Component Vehicle name from Spring Context is: Honda


Destroying Vehicle Bean

Adding New Beans Programmatically


Sometimes we want to create new instances of an object and add them into the Spring context
based on a programming condition. For the same, from Spring 5 version, a new approach is
provided to create beans programmatically by invoking the registerBean() method present inside
the ApplicationContext object.

This method gives us the power to dynamically add beans to the context after the context has
already been initialized.

The

Let's break down the registerBean() method call:

Generated java
context.registerBean("volkswagen", Vehicle.class, volkswagenSupplier);

Java

1.​ (The ApplicationContext instance object): This is the first part, representing the Spring
container itself, on which we invoke the method.
2.​ (The name we want to give): This is the first argument, a String that defines the name of
the bean we are adding to the Spring context.
3.​ (Type of the Bean we are creating): The second argument is the Class type of the bean.
4.​ (The supplier returning the object instance): The third and most important argument is a
Supplier. A Supplier is a functional interface in Java (java.util.function.Supplier)
that has a single method, get(). It provides the logic for creating the actual object instance.

Example of Conditional Bean Registration:

We can use this method inside conditional logic to register different beans based on runtime
conditions.

Generated java

// Assuming 'context' is an instance of AnnotationConfigApplicationContext


// and we have suppliers defined for different vehicles:
// Supplier<Vehicle> audiSupplier = () -> { ... create and return Audi ... };
// Supplier<Vehicle> volkswagenSupplier = () -> { ... create and return VW ... };

int randomNumber = new Random().nextInt(10);

if ((randomNumber % 2) == 0) {
context.registerBean(beanName: "volkswagen",
Vehicle.class, volkswagenSupplier);
} else {
context.registerBean(beanName: "audi",
Vehicle.class, audiSupplier);
}

Java

In this scenario, a Volkswagen or Audi Vehicle will be created and registered as a bean inside the
Spring Context based on whether the random number is even or odd.

Adding New Beans Using XML Configs


In the initial versions of Spring, the bean and other configurations used to be done using XML. But
over time, the Spring team brought annotation-based configurations to make developers' lives easy.
Today, we can see XML configurations only in the older applications built on initial versions of Spring.

However, it is good to understand how to create a bean inside the Spring context using XML-style
configurations. This will be useful if you ever encounter a scenario where you need to work on a
project based on initial versions of Spring.

To load configurations from an XML file, you use a different type of ApplicationContext:
ClassPathXmlApplicationContext.

XML Configuration (

This file is typically placed in the src/main/resources directory.

Generated xml

<?xml version="1.0" encoding="UTF-8"?>


<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="vehicle" class="com.example.beans.Vehicle">


<property name="name" value="Honda"/>
</bean>

</beans>

Xml

●​ The <bean> tag declares a new bean.


●​ The id attribute is the unique name of the bean.
●​ The class attribute is the fully qualified name of the Java class.
●​ The <property> tag is used to inject dependencies. Here, it calls the setName() method on
the Vehicle object and passes "Honda" as the value.

Java Code to Load XML Context:

Generated java

// Create a context from the XML file on the classpath


var context = new ClassPathXmlApplicationContext("beans.xml");
Vehicle vehicle = context.getBean(Vehicle.class);
System.out.println("Vehicle name from Spring Context is: " + vehicle.getName());

Java

Output on Console:

Generated code

Vehicle name from Spring Context is: Honda


Chapter 4: Wiring and Autowiring: Connecting the Dots
Introduction to Beans Wiring inside Spring
Inside Java web applications, it's rare for objects to work in isolation. Usually, objects delegate
certain responsibilities to other objects. For example, a VehicleController handles incoming web
requests but delegates the business logic to a VehicleService. The VehicleService in turn
delegates the database interaction to a VehicleDAO. In these scenarios, objects will have
dependencies on others.

In very simple lines, when we create various beans using Spring, it is our responsibility to
understand the dependencies that beans have and wire them. This concept is called
Wiring/Autowiring.

Visualizing the Dependency Chain:​


A typical flow looks like this:​
VehicleController Object -> VehicleService Object -> VehicleDAO Object

The Impact of Wiring on the Spring Context:

●​ Spring Context The context contains individual, disconnected beans. The Controller,
Service, and DAO objects exist, but they have no knowledge of each other.
●​ Spring Context with Wiring & DI: The context not only contains the beans but also
understands their relationships. It has injected the Service object into the Controller and
the DAO object into the Service, creating a fully connected and functional application graph.

No Wiring Scenario Inside Spring


Consider a scenario where we have two Java classes, Person and Vehicle. The Person class has
a dependency on the Vehicle. Based on the below code, we are creating the beans inside the
Spring Context, but no wiring will be done. Due to this, both the Person and Vehicle beans are
present inside the Spring context without knowing about each other.
Class Definitions:

Generated java

public class Vehicle {


private String name;
// Getters and setters
}

public class Person {


private String name;
private Vehicle vehicle; // The dependency
// Getters and setters
}

Java

Configuration (No Wiring):

Generated java

@Configuration
public class ProjectConfig {
@Bean
public Vehicle vehicle() {
Vehicle vehicle = new Vehicle();
vehicle.setName("Toyota");
return vehicle;
}

@Bean
public Person person() {
Person person = new Person();
person.setName("Lucy");
return person;
}
}
Java

In this setup, Spring creates a Vehicle bean and a Person bean. However, the vehicle field inside
the Person bean is null. The Person and Vehicle beans are present in the context, but no relation
is established.

Wiring Beans Using a Method Call


Here in the below code, we are trying to wire, or establish a relationship between, Person and
Vehicle by invoking the vehicle() bean method from the person() bean method. Now,
inside the Sprint Context, the person owns the vehicle.

A crucial point to understand is that Spring will make sure to have only 1 Vehicle bean created,
and the same Vehicle bean will be first created as person has a dependency on it. Spring is smart
enough to not execute the vehicle() method a second time; instead, it intercepts the call and
returns the already-created bean from the context.

Configuration (Wiring with Method Call):

Generated java

@Configuration
public class ProjectConfig {
@Bean
public Vehicle vehicle() {
Vehicle vehicle = new Vehicle();
vehicle.setName("Toyota");
System.out.println("Vehicle bean created by Spring");
return vehicle;
}

@Bean
public Person person() {
Person person = new Person();
person.setName("Lucy");
// Here we establish the relationship
person.setVehicle(vehicle()); // Calling the other @Bean method
System.out.println("Person bean created by Spring");
return person;
}
}

Java
Main Application:

Generated java

var context = new AnnotationConfigApplicationContext(ProjectConfig.class);


Person person = context.getBean(Person.class);
System.out.println("Person name from Spring Context is: " + person.getName());
System.out.println("Vehicle name from Spring Context is: " + person.getVehicle().getName());
System.out.println("Vehicle that Person own is: " + person.getVehicle().getName());

Java

Output on Console:

Generated code

Vehicle bean created by Spring


Person bean created by Spring
Person name from Spring Context is: Lucy
Vehicle name from Spring Context is: Toyota
Vehicle that Person own is: Toyota

Wiring Beans Using Method Parameters


Here in the below code, we are trying to wire or establish a relationship between Person and
Vehicle by passing the vehicle as a method parameter to the person() bean method. Now,
inside the Spring Context, the person owns the vehicle.

●​ Spring injects the vehicle bean into the person bean through Dependency Injection.
●​ Spring will make sure to have only 1 Vehicle bean created, and also the vehicle bean will
be created first as the person bean has a dependency on it.

This approach is generally preferred over method calls as it makes the dependencies more explicit.

Configuration (Wiring with Method Parameters):

Generated java

@Configuration
public class ProjectConfig {
@Bean
public Vehicle vehicle() {
Vehicle vehicle = new Vehicle();
vehicle.setName("Toyota");
System.out.println("Vehicle bean created by Spring");
return vehicle;
}

/* Spring will see that this method needs a Vehicle,


find the Vehicle bean in the context, and pass it in. */
@Bean
public Person person(Vehicle vehicle) {
Person person = new Person();
person.setName("Lucy");
person.setVehicle(vehicle); // Using the injected parameter
System.out.println("Person bean created by Spring");
return person;
}
}

Java

The main application code and the console output will be identical to the previous "Method Call"
example, demonstrating that this is just a cleaner, more declarative way to achieve the same result.
Chapter 5: Autowiring Beans in Depth
Manual wiring with @Bean methods works, but for larger applications, autowiring is the standard. It
lets Spring automatically discover and inject dependencies.

Inject Beans using @Autowired on Class Fields


The @Autowired annotation on a field, setter method, or constructor is used to auto-wire the beans.
This is "injecting beans" (Objects) at runtime by the Spring Dependency Injection mechanism.

With the below code, Spring injects/auto-wires the vehicle bean to the person bean through a
class field and dependency injection.

However, the below style (field injection) is not recommended for production usage as we
can't mark the fields as final, which is a best practice for dependencies. It also makes
testing more difficult.

To avoid a NoSuchBeanDefinitionException if the bean is not available during the autowiring


process, we can use @Autowired(required = false).

Example (Field Injection):

Generated java

@Component
public class Vehicle {
private String name = "Toyota";
//...getter
}

@Component
public class Person {
private String name = "Lucy";

@Autowired // Field Injection


private Vehicle vehicle;

//...getters
}

Java
Main Application & Output:​
The application logic would be the same as before, and the output would confirm that the Person
bean now has a reference to the Vehicle bean.

Inject Beans using @Autowired on Setter Method


With the below code, Spring injects/auto-wires the vehicle bean to the person bean through a
setter method and dependency injection.

This style is also not generally recommended for production usage as we can't mark the fields
as final and it's not as readable as constructor injection.

Example (Setter Injection):

Generated java

@Component
public class Person {
private String name = "Lucy";
private Vehicle vehicle;

@Autowired
public void setVehicle(Vehicle vehicle) {
this.vehicle = vehicle;
}

//...getters
}

Java

Inject Beans using @Autowired with Constructor (Best Practice)


With the below code, Spring injects/auto-wires the vehicle bean to the person bean through a
constructor and dependency injection. This is the recommended approach. It ensures all required
dependencies are present when the object is created and allows the dependency fields to be
declared as final.

From Spring version 4.3, when we only have one constructor in the class, the @Autowired
annotation is optional. Spring will automatically use that constructor for injection.
Example (Constructor Injection):

Generated java

@Component
public class Person {
private final String name = "Lucy";
private final Vehicle vehicle; // Dependency can be final!

@Autowired // This annotation is optional here


public Person(Vehicle vehicle) {
System.out.println("Person bean created by Spring");
this.vehicle = vehicle;
}

//...getters
}

Java

How Autowiring Works with Multiple Beans of the Same Type


This is a critical scenario to understand.

●​ By default, Spring tries autowiring with class type. But this approach will fail if the same
class type has multiple beans.
●​ If the Spring context has multiple beans of the same class type like below, then Spring will try
to auto-wire based on the parameter name/field name that we use while configuring
autowiring annotation.

Let's walk through the steps Spring takes to resolve ambiguity.

Step 1: Match by Parameter/Field Name

In the below scenario, we used 'vehicle1' as the constructor parameter. Spring will try to
auto-wire with the bean which has the same name.

Generated java

// Context contains beans: "vehicle1", "vehicle2", "vehicle3"

@Component
public class Person {
private String name = "Lucy";
private final Vehicle vehicle;
// The parameter name 'vehicle1' is important!
@Autowired
public Person(Vehicle vehicle1) {
System.out.println("Person bean created by Spring");
this.vehicle = vehicle1;
}
}

Java

In this case, Spring will match the parameter name vehicle1 to the bean named vehicle1 in the
context and inject it.

Step 2: Match by @Primary

If the parameter name/field name that we use while configuring the autowiring annotation is not
matching with any of the bean names, then Spring will look for the bean which has @Primary
configured.

In the below scenario, we used 'vehicle' as the constructor parameter. Spring will try to auto-wire
with the bean which has the same name, and since it can't find a bean with the name vehicle, it will
look for the bean with @Primary configured, which is vehicle3.

Generated java

// Context contains "vehicle1", "vehicle2", "vehicle3" (Primary Bean)

@Component
public class Person {
private String name = "Lucy";
private final Vehicle vehicle;

// Parameter name is now 'vehicle', which doesn't match any bean name.
@Autowired
public Person(Vehicle vehicle) {
System.out.println("Person bean created by Spring");
this.vehicle = vehicle;
}
}

Java

Here, Spring will inject the vehicle3 bean because it's marked as @Primary.
Step 3: Match by @Qualifier

If the parameter name/field name that we use is not matching with any of the bean names and even
a @Primary bean is not configured, then Spring will look if the @Qualifier annotation is used with
the bean name matching with Spring context bean names.

In the below scenario, we used the @Qualifier annotation. Spring will try to auto-wire with the bean
which has the same name as mentioned in the qualifier.

Generated java

// Context contains "vehicle1", "vehicle2", "vehicle3"

@Component
public class Person {
private String name = "Lucy";
private final Vehicle vehicle;

// Explicitly ask for the bean named "vehicle2"


@Autowired
public Person(@Qualifier("vehicle2") Vehicle vehicle) {
System.out.println("Person bean created by Spring");
this.vehicle = vehicle;
}
}

Java

This is the most explicit way to resolve ambiguity. Spring will inject the bean named vehicle2,
ignoring all other rules.

Understanding & Avoiding Circular Dependencies


A Circular dependency will happen if 2 beans are waiting for each to be created inside the Spring
context in order to do auto-wiring.

Consider the below scenario, where Person has a dependency on Vehicle and Vehicle has a
dependency on Person. In such scenarios, Spring will throw an
UnsatisfiedDependencyException due to the circular reference.

As a developer, it is our responsibility to make sure we are defining the configurations/dependencies


in a way that will not result in circular dependencies.
Code Causing a Circular Dependency:

Generated java

@Component
public class Person {
private String name = "Lucy";
private Vehicle vehicle;

@Autowired // Using setter injection for illustration


public void setVehicle(Vehicle vehicle) {
this.vehicle = vehicle;
}
}

@Component
public class Vehicle {
private String name;
private Person person;

@Autowired // The other side of the circle


private void setPerson(Person person) {
this.person = person;
}
}

Java

When Spring tries to create these beans, it enters a deadlock:

●​ To create Person, it needs a Vehicle bean.


●​ To create Vehicle, it needs a Person bean.
●​ But the Person bean is still in creation, waiting for a Vehicle...

Spring detects this and fails fast with an exception, preventing a stack overflow. This is almost
always a sign of a design flaw that needs to be fixed by refactoring.

Assignment Related to Beans, Autowiring, DI


This assignment brings together all the concepts of beans, dependencies, and autowiring.

The Scenario:

●​ A Person Bean has a dependency on a Vehicle Bean.


●​ A Vehicle Bean has a dependency on a VehicleServices Bean, to play music and move
the vehicle.
●​ VehicleServices bean depends on the implementations of Speakers and Tyres to serve
vehicle bean requests.
●​ There are multiple implementations of Speakers (e.g., SonySpeakers, BoseSpeakers)
which implement a Speakers interface with a makeSound() method.
●​ There are multiple implementations of Tyres (e.g., BridgeStoneTyres, MichelinTyres)
which implement a Tyres interface with a rotate() method.

The Goal:​
Your application should play music from one of the Speakers implementations and move using one
of the Tyres implementations. It should also give flexibility to switch between the
implementations easily (e.g., using @Primary or @Qualifier).
Chapter 6: Understanding Bean Scopes
In Spring, the scope of a bean defines its lifecycle and visibility. It answers the question: "When I ask
for this bean, do I get a new instance every time, or do I get the same shared instance?" Spring
defines several scopes.

The five primary bean scopes inside Spring are:

1.​ Singleton
2.​ Prototype
3.​ Request
4.​ Session
5.​ Application

Singleton Bean Scope


●​ Singleton is the default scope of a bean in Spring. In this scope, for a single bean
definition, we always get the same instance when we refer to or autowire it. The Spring IoC
container creates exactly one instance of the object per bean definition.
●​ Important Distinction: Unlike the classic Singleton design pattern where we have only 1
instance in the entire app, inside the Spring Singleton scope, Spring will make sure to have
only 1 instance per unique bean definition. For example, if you have multiple bean
definitions of the same type (like our vehicle1, vehicle2 examples), then the Spring
Singleton scope will maintain 1 instance for each of those definitions.

Example of Singleton Scope:

To explicitly declare a bean's scope, you use the @Scope annotation.

Generated java

@Component
// This is redundant as Singleton is the default, but shown for clarity.
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class VehicleServices {
// ...
}

// Main application logic


var context = new AnnotationConfigApplicationContext(ProjectConfig.class);
VehicleServices vehicleServices1 = context.getBean(VehicleServices.class);
VehicleServices vehicleServices2 = context.getBean(VehicleServices.class);

System.out.println("Are the two beans the same? " + (vehicleServices1 == vehicleServices2));

Java

Output on Console:

Generated code

Are the two beans the same? true

The output is true because both variables vehicleServices1 and vehicleServices2 refer to the
exact same bean instance inside the Spring context.

The Problem of State in Singleton Beans: Race Conditions


A race condition occurs when two or more threads access a shared variable at the same time, and
at least one of them modifies the variable.

●​ The first thread reads the variable.


●​ The second thread reads the same value from the variable before the first thread has written
its change.
●​ Then the first thread and second thread perform their operations on the value, and they
"race" to see which thread can write the value last to the shared variable.
●​ The value of the thread that writes its value last is preserved, because the thread is writing
over the value that the previous thread wrote.

Example Scenario:

Imagine a Singleton bean that manages table reservations in a restaurant. It has a Map to store
which user has reserved which table.

Generated java

// This is a stateful Singleton bean - DANGEROUS!


@Component
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class RestaurantService {
// Shared, mutable state
private Map<String, String> reservedTables = new HashMap<>();
public void reserveTable(String table, String user) {
if (!reservedTables.containsKey(table)) {
reservedTables.put(table, user);
}
}
}

Java

Now, consider two concurrent users (Thread 1 and Thread 2) trying to reserve "table1":

1.​ Thread 1 executes !reservedTables.containsKey("table1"). It evaluates to true.


2.​ Context Switch: The OS pauses Thread 1 right before it can execute the put method.
3.​ Thread 2 executes !reservedTables.containsKey("table1"). It also evaluates to true
because Thread 1 hasn't modified the map yet.
4.​ Thread 2 executes reservedTables.put("table1", "USER2"). The reservation is made.
5.​ Context Switch: The OS resumes Thread 1.
6.​ Thread 1 executes reservedTables.put("table1", "USER1"). It overwrites the
reservation made by Thread 2.

The result is a corrupt state. Both users think they have the table, but only USER1's reservation is
actually stored.

Use Cases of Singleton Beans (And How to Use Them Safely)


Because of the risk of race conditions, there are important rules for using Singleton beans.

●​ Since the same instance of a singleton bean will be used by multiple threads inside your
application, it is very important that these beans are immutable or stateless. A stateless
bean has no fields that store data specific to one execution (like our reservedTables map).
It may have dependencies on other beans, but its own state does not change.
●​ This scope is most suitable for beans that handle service layer or repository layer
business logics, as these are typically stateless.
●​ There are ways to avoid race conditions in stateful singleton beans with the help of
synchronization. However, this is not recommended, since it brings a lot of complexity and
performance issues inside your app. So please don't try to build mutable singleton
beans.

The Rule of Thumb: Singleton beans should be stateless.


Eager & Lazy Instantiation
●​ By default, Spring will create all the singleton beans eagerly during the startup of the
application itself. This is called Eager instantiation.
●​ We can change this default behavior to initialize the singleton beans lazily only when the
application is trying to refer to the bean for the first time. This approach is called Lazy
instantiation.

To enable lazy instantiation, we use the @Lazy annotation.

Sample Demo of Lazy Instantiation:

Generated java

@Component("personBean")
@Lazy // This bean will not be created on startup
public class Person {
public Person() {
System.out.println("Person bean created by Spring");
}
}

// Main Application
var context = new AnnotationConfigApplicationContext(ProjectConfig.class);
System.out.println("Before retrieving the Person bean from the Spring Context");
Person person = context.getBean(Person.class);
System.out.println("After retrieving the Person bean from the Spring Context");

Java

Output on Console:

Generated code

Before retrieving the Person bean from the Spring Context


Person bean created by Spring
After retrieving the Person bean from the Spring Context
Notice that "Person bean created by Spring" is printed between the other two lines, proving that the
bean was only created when context.getBean() was called.

Eager Vs Lazy Instantiation


Eager Instantiation Lazy Instantiation

This is the default behavior inside the This is not a default behavior and needs to
Spring framework. be configured explicitly using @Lazy.

The singleton bean will be created during The singleton bean will be created when the
the startup of the application. app is trying to refer to the bean for the
first time.

The server will not start if a bean is not The application will throw an exception at
able to be created due to any dependent runtime if bean creation is failed due to any
exceptions. (Fail-fast, usually good) dependent exceptions. (Fail-late, can be
bad)

Spring context will occupy a lot of memory The performance will be impacted if we try
if we try to use eager for all beans inside to use lazy for all beans inside an
an application. application.

Eager can be followed for all the beans Lazy can be followed for the beans that are
which are required very commonly used in a very remote scenario inside an
inside an application. application.

Prototype Bean Scope


●​ With the prototype scope, every time we request a reference of a bean, Spring will create a
new object instance and provide the same.
●​ The prototype scope is rarely used inside applications. We can use this scope only in the
scenarios where your bean will frequently change the state of the data, which will result in
race conditions inside a multi-threaded environment. Using the prototype scope will not
create any race conditions because each thread gets its own instance.
Example of Prototype Scope:

Generated java

@Component
@Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class VehicleServices {
public VehicleServices() {
System.out.println("VehicleServices prototype bean created");
}
}

// Main Application
var context = new AnnotationConfigApplicationContext(ProjectConfig.class);

VehicleServices vs1 = context.getBean(VehicleServices.class);


VehicleServices vs2 = context.getBean(VehicleServices.class);

System.out.println("Are the two beans the same? " + (vs1 == vs2));

Java

Output on Console:

Generated code

VehicleServices prototype bean created


VehicleServices prototype bean created
Are the two beans the same? false

The constructor message is printed twice, and the comparison is false, proving that two separate
instances were created.

Important Note on Lifecycle: For prototype-scoped beans, Spring does not manage the complete
lifecycle. The container instantiates, configures, and assembles the object and hands it to the client.
After that, the client is responsible for the object. Spring does not call
Singleton Vs Prototype
Singleton Scope Prototype Scope

This is the default scope Need to explicitly configure using


inside the Spring framework. @Scope(BeanDefinition.SCOPE_PROTOTYPE).

The same object instance A new object instance will be returned every time we refer to
will be returned every time a bean inside the code.
we refer to a bean inside the
code.

We can configure the beans Spring always creates the new object when we try to refer
to be created during the to the bean. No eager instantiation is possible.
startup or when the first time
they are referred.

Immutable/Stateless objects Mutable/Stateful objects can be ideal for the prototype scope
are ideal for the Singleton to avoid race conditions.
scope.

Most commonly used Very rarely used scope.


scope.
Chapter 7: Aspect-Oriented Programming (AOP)
Aspect-Oriented Programming (AOP) complements Object-Oriented Programming (OOP) by
providing another way of thinking about program structure. While OOP's main unit of modularity is
the class, AOP's is the aspect. Aspects enable the modularization of concerns that cut across
multiple types and objects. Such concerns are often termed cross-cutting concerns.

●​ An aspect is simply a piece of code the Spring framework executes when you call specific
methods inside your app.
●​ Spring AOP enables Aspect-Oriented Programming in Spring applications. In AOP, aspects
enable the modularization of concerns such as transaction management, logging, or security
that cut across multiple types and objects.
●​ AOP provides the way to dynamically add the cross-cutting concern before, after, or
around the actual logic using simple pluggable configurations.
●​ AOP helps in separating and maintaining many non-business logic related code like
logging, auditing, security, transaction management.
●​ AOP is a programming paradigm that aims to increase modularity by allowing the
separation of cross-cutting concerns. It does this by adding additional behavior to existing
code without modifying the code itself.
AOP in Action: Cleaning Up Business Logic
Consider a method in VehicleServices that moves a vehicle. Without AOP, the business logic gets
cluttered with non-business logic code like logging and timing.

Before AOP (Cluttered Code):

Generated java

public String moveVehicle(boolean started) {


Instant start = Instant.now();
logger.info("method execution start");
String status = null;

if(started) {
status = tyres.rotate(); // <-- The *actual* business logic
} else {
logger.log(Level.SEVERE, "Vehicle not started to perform the operation");
}

logger.info("method execution end");


Instant finish = Instant.now();
long timeElapsed = Duration.between(start, finish).toMillis();
logger.info("Time took to execute the method : " + timeElapsed);
return status;
}

Java

There is so much non-business logic code along with the main business logic.

After AOP (Clean Code):

With AOP magic, all the non-business logic is moved to a different location (an aspect), which will
make the method clean and clear.

Generated java

public String moveVehicle(boolean started) {


return tyres.rotate();
}

Java

AOP Jargons: The 3 Ws


When we define an Aspect or are doing configurations, we need to follow the WWW (3 Ws):

1.​ WHAT -> Aspect: What code or logic do we want Spring to execute when we call a specific
method? This logic is encapsulated in an Aspect.
2.​ WHEN -> Advice: When does the Spring need to execute the given Aspect? For example, is
it before or after the method call? This is called an Advice.
3.​ WHICH -> Pointcut: Which method inside the App does the framework need to intercept
and execute the given Aspect? This is called a Pointcut.

Other Key Terms:

●​ Join point: Defines the event that triggers the execution of an aspect. Inside Spring, this
event is always a method call.
●​ Target object: The bean that declares the method/pointcut which is intercepted by an
aspect.

Typical Scenario of AOP Implementation:

Let's break down a requirement using these terms:

"A Developer wants some logic to be executed before each execution of the method playMusic()
present inside the bean VehicleServices."

●​ Aspect: some logic (e.g., logging "Starting to play music...")


●​ Advice: before
●​ Joinpoint: execution
●​ Pointcut: playMusic() method
●​ Target Object: VehicleServices bean

Weaving inside AOP


●​ When we are implementing AOP inside our App using the Spring framework, it will intercept
each method call and apply the logic defined in the Aspect.
●​ But how does this work? Spring does this with the help of a proxy object. So when we try to
invoke a method inside a bean, instead of directly giving a reference of the bean, Spring will
give a proxy object that will manage the call to a method and apply the aspect logic. This
process is called Weaving.

The Flow:
●​ Without AOP: A method call goes directly to the VehicleServices Bean. No interception
by Spring.
●​ With AOP: A method call is intercepted by a Proxy object of VehicleServices Bean.
This proxy object first executes the aspect logic (the advice). After that, it delegates the
actual method call to the target VehicleServices bean. The method execution is
intercepted by the proxy object, the aspect will be executed, and post that the actual method
invocation will happen.

Advice Types Inside AOP


Spring AOP provides five types of advice, which correspond to the "WHEN" of AOP. These are
implemented using annotations.

●​ @Before: Before advice runs before a matched method execution.


●​ @AfterReturning: After returning advice runs when a matched method execution
completes normally.
●​ @AfterThrowing: After throwing advice runs when a matched method execution exits by
throwing an exception.
●​ @After: After (finally) advice runs no matter how a matched method execution exits
(normally or by exception).
●​ @Around: Around advice runs "around" a matched method execution. It has the opportunity
to do work both before and after the method runs and to determine when, how, and even if
the method actually gets to run at all. This is the most powerful type of advice.

Configuring Advices inside AOP


There are two main approaches to defining the "WHICH" (the Pointcut).

Approach 1: Execution Expression

We can use the AspectJ pointcut expression to provide details to Spring about what kind of
methods it needs to intercept by mentioning details about the modifier, return type, name pattern,
package name pattern, params pattern, exceptions pattern, etc.

The general structure of an execution expression is:​


execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?
name-pattern(param-pattern) throws-pattern?)

●​ modifiers-pattern: e.g., public, private


●​ ret-type-pattern: The return type, e.g., * for any, void, String
●​ declaring-type-pattern: The class/package, e.g., com.example.services.*
●​ name-pattern: The method name, e.g., * for any, save* for methods starting with save.
●​ param-pattern: The method parameters, e.g., () for no params, (..) for any params.
●​ throws-pattern: The exception thrown.

Example Configuration:

Generated java

// Configuration Class
@Configuration
@ComponentScan(basePackages = {"com.example.implementation",
"com.example.services", "com.example.aspects"})
@EnableAspectJAutoProxy // This is crucial to enable AOP
public class ProjectConfig {
}

// Aspect Class
@Aspect
@Component
public class LoggerAspect {

// An @Around advice that intercepts all methods in the com.example.services package


@Around("execution(* com.example.services.*.*(..))")
public void log(ProceedingJoinPoint joinPoint) throws Throwable {
// Before logic
System.out.println("Method will execute");

joinPoint.proceed(); // This invokes the target method

// After logic
System.out.println("Method executed");
}
}

Java

Approach 2: Using Annotations


Alternatively, we can create our own custom annotation and use that to mark the methods we want
to intercept. This is often cleaner and easier to read than complex execution expressions.

Step 1: Create an annotation type.

Generated java

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface LogAspect {
}

Java

Step 2: Mention the same annotation on top of the method which we want to intercept using
AOP.

Generated java

public class MyService {


@LogAspect // Mark this method for AOP
public String playMusic(boolean started, String song) {
// Business Logic
return "Playing " + song;
}
}

Java

Step 3: Use the annotation details to configure the pointcut on top of the aspect method to
advise.

Generated java

@Aspect
@Component
public class LoggerAspect {

// This pointcut now targets any method annotated with @LogAspect


@Around("@annotation(com.example.interfaces.LogAspect)")
public void logWithAnnotation(ProceedingJoinPoint joinPoint) throws Throwable {
// ... aspect logic ...
joinPoint.proceed();
// ... aspect logic ...
}
}

Java
Chapter 8: Building Web Apps with Spring MVC
Now that we have a solid grasp of the Spring Core, we can move on to building web applications.
This is where the Spring MVC (Model-View-Controller) module comes into play.

Overview of a Web App


A modern web application is a client-server system.

Web Clients: These are the applications that users interact with.

●​ Browser: (e.g., Chrome, Firefox) running on a desktop or laptop.


●​ Postman: A developer tool used for testing APIs.
●​ Mobile: A native app running on a smartphone.

Web Server: This is where the application code resides and runs. It consists of:

●​ Frontend: The UI part of the application, often built with technologies like Angular, React, or
just HTML/CSS/JavaScript.
●​ Backend: The server-side logic, built with frameworks like Spring. This part handles
business logic, database interactions, etc.
●​ Web Application: The combination of frontend and backend deployed on the server.

The Basic Flow:

1.​ The Web client sends a Request using protocols like HTTP to the Web Application, asking
for some data (e.g., a list of images, text, etc.).
2.​ The Web server where the web app is deployed receives the client request and processes
the data it receives. Post that, it will respond to the client's request in the format of HTML,
JSON, etc.
3.​ In Java web apps, a Servlet Container (which is part of the Web Server) takes care of
translating the HTTP messages for Java code to understand. One of the most used servlet
containers is Apache Tomcat.
4.​ The Servlet Container converts the HTTP messages into a ServletRequest object and
hands it over to the Servlet method as a parameter. Similarly, a ServletResponse is
returned as an output to the Servlet Container from the Servlet.
Types of Web Applications:​
Usually, Web Applications can be:

1.​ Only Frontend (Static Web Apps): Simple websites with no backend logic.
2.​ Only Backend (APIs): Services that only provide data (e.g., in JSON format) to other
applications.
3.​ Frontend + Backend (E-commerce Apps): Full-stack applications that manage both the UI
and the server-side logic.

Role of Servlets Inside Web Apps


Before Spring:

1.​ Before Spring, the developer had to create a new servlet instance, configure it in the servlet
container (usually via an web.xml file), and assign it to a specific URL path. You would have
many servlets, like LoginServlet, HomeServlet, DashboardServlet, each mapped to a
different URL.
2.​ When the client sends a request, the Servlet Container (like Tomcat) calls a method of the
servlet associated with the path the client requested. The servlet gets the values on the
request and builds the response that Tomcat sends back to the client.

With Spring:

1.​ With Spring, it defines a single, central servlet called the DispatcherServlet which
maintains all the URL mapping inside a web application. This acts as the "front controller."
2.​ The servlet container calls this DispatcherServlet for any client request, allowing the
Spring framework to manage the request and the response. This way, Spring internally does
all the magic for Developers without the need of defining multiple servlets inside a Web app.

Evolution of Web Apps inside the Java Ecosystem


The way we build web applications has changed dramatically over the decades.

●​ Somewhere in 2000: Technologies included JSP/JSF, Servlets, SOAP, HTML/CSS, JDBC,


and J2EE. There were no Web Design patterns and frameworks support present in the
2000s. So all the web application code was written in such a way that all the layers like
Presentation, Business, and Data layers were tightly coupled.
●​ Somewhere in 2010: With the help and invention of design patterns like MVC and
frameworks like Spring, Struts, and Hibernate, developers started building web applications
by separating the layers of Presentation, Business, and Data. But all the code was still
deployed into a single jumbo server as a monolithic application. Technologies included
JSP/JSF, jQuery/Bootstrap, MVC, HTML/CSS, SOAP/REST, and ORM.
●​ Somewhere in 2020: With the invention of UI frameworks like Angular and React, and new
trends like Microservices and Containers, developers started building web applications by
separating the UI and backend layers. The code is also deployed into multiple servers
using containers and cloud. Key technologies include Angular, React JS, HTML/CSS, REST,
ORM/JPA, Cloud, Microservices, and Docker/K8s.

Developing Web Applications Using Spring


Using the modern Spring framework, there are two primary approaches to building web applications.

Approach 1: Traditional Web App (Server-Side Rendering)

●​ Web Apps which hold UI elements like HTML, CSS, JS and backend logic.
●​ Here the App is responsible to fully prepare the view along with data in response to a client
request. The server sends back a complete HTML page.
●​ The Spring projects used for this approach will be: Spring Core, Spring MVC, SpringBoot,
Spring Data, Spring Rest, Spring Security.

Approach 2: Backend API (Separated Frontend)

●​ Web Apps which hold only backend logic. These Apps send data like JSON to separate UI
Apps built based on Angular, React, etc.
●​ Here the App is responsible to only process the request and respond with only data,
ignoring the view. The frontend is responsible for rendering that data.
●​ The Spring projects used for this approach will be: Spring Core, SpringBoot, Spring Data,
Spring Rest, Spring Security.

Spring MVC is the key differentiator between these two approaches. Approach 1 uses Spring
MVC's view-rendering capabilities heavily, while Approach 2 uses Spring MVC primarily to build
REST endpoints.
Chapter 9: Spring Boot: The Hero of the Spring
Framework
While the Spring Framework is incredibly powerful, setting up a new project historically involved a lot
of manual configuration: XML files, dependency version management, and servlet container setup.
Spring Boot was created to solve this.

1.​ Spring Boot was introduced in April 2014 to reduce some of the burdens while developing a
Java web application.
2.​ Before SpringBoot, a developer needed to configure a servlet container, establish the link
between Tomcat and the Dispatcher servlet, deploy into a server, and define a lot of
dependencies.
3.​ But with SpringBoot, we can create Web Apps skeletons within seconds or at least in 1-2
mins 😊. It helps in eliminating all the configurations we need to do.
4.​ Spring Boot is now one of the most appreciated projects in the Spring ecosystem. It helps us
to create Spring apps more efficiently and focus on the business code.
5.​ SpringBoot is a mandatory skill now due to the latest trends like Full Stack Development,
Microservices, Serverless, Containers, Docker etc.

Before & After SpringBoot


The Old Way (Before SpringBoot):

●​ 😞 Configure a Maven/Gradle project with all the dependencies needed, manually ensuring
versions are compatible.
●​ 😞 Understand how servlets work & configure the DispatcherServlet inside .
web.xml
●​ 😞 Package the web application into a WAR file. Deploy the same into a server like Tomcat.
●​ 😞 Deal with complicated class loading strategies, application monitoring, and management.
The New Way (With SpringBoot):

●​ 😄 Spring Boot automatically configures the bare minimum components of a Spring


application.
●​ 😄 Spring Boot applications embed a web server (like Tomcat) so that we do not require an
external application server.
●​ 😄 Spring Boot provides several useful production-ready features out of the box to monitor
and manage the application.
The Magic of SpringBoot: Important Features
SpringBoot achieves its simplicity through a few key features.

●​ SpringBoot Starters: Spring Boot groups related dependencies used for a specific purpose
as starter projects. We don't need to figure out all the must-have dependencies we need to
add to our project for one particular purpose nor which versions we should use for
compatibility. You just include the starter, and you're done.
○​ Example: spring-boot-starter-web automatically brings in everything needed for
a web application, including Spring MVC, Tomcat, and JSON handling libraries.
●​
●​ Autoconfiguration: Based on the dependencies present in the classpath, Spring Boot will
guess and auto-configure the spring beans, property configurations, etc. However,
auto-configuration backs away from the default configuration if it detects user-configured
beans with custom configurations. To achieve autoconfiguration, SpringBoot follows the
convention-over-configuration principle.
●​ Actuator & DevTools:
○​ Actuator: Spring Boot provides a pre-defined list of actuator endpoints. Using this
production-ready feature, we can monitor app health, metrics, etc.
○​ DevTools: Includes features such as automatic detection of application code
changes, a LiveReload server to automatically refresh any HTML changes to the
browser all without a server restart.
●​

Getting Started with SpringBoot: Quick Recap


Here are the essential annotations and properties to get a Spring Boot application running.

1.​ https://start.spring.io is the website which can be used to generate web projects
skeleton based on the dependencies required for an application.
2.​ We can identify the Spring Boot main class by looking for an annotation
@SpringBootApplication.
3.​ A single @SpringBootApplication annotation can be used to enable those three features,
that is:
○​ @EnableAutoConfiguration: enable Spring Boot's auto-configuration mechanism.
○​ @ComponentScan: enable @Component scan on the package where the application is
located.
○​ @SpringBootConfiguration: enable registration of extra beans in the context or
the import of additional configuration classes. An alternative to Spring's standard
@Configuration annotation.
4.​
5.​ The @RequestMapping annotation provides "routing" information. It tells Spring that any
HTTP request with the given path should be mapped to the corresponding method.
6.​ server.port and server.servlet.context-path properties can be mentioned inside the
application.properties file to change the default port number and context path of a web
application.
7.​ Mentioning server.port=0 will start the web application at a random port number every
time.
8.​ Mentioning debug=true will print the Autoconfiguration report on the console. We can
mention the exclusion list as well for SpringBoot auto-configuration by using the below
config:
9.​ Generated java

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })

Java
Quick Tip: Multiple Paths
Do you know, we can configure multiple paths against a single method using Spring MVC
annotations?

Generated java

@Controller
public class HomeController {

// This method will handle requests to "/", "/home", etc.


@RequestMapping(value={"", "/", "/home"})
public String displayHomePage(Model model) {
// Business Logic
return "home.html";
}
}

Java
Chapter 10: Dynamic Views with Thymeleaf & DevTools
Introduction to Thymeleaf
While Spring can serve REST APIs, it can also render dynamic server-side views. For this, it needs
a template engine.

●​ Thymeleaf is a modern server-side Java template engine for both web and standalone
environments. This allows developers to build dynamic content inside the web applications.
●​ Thymeleaf has great integration with Spring, especially with Spring MVC, Spring Security,
etc.
●​ The Thymeleaf + Spring integration packages offer a
SpringResourceTemplateResolver implementation which uses all the Spring
infrastructure for accessing and reading resources in applications, and which is the
recommended implementation in Spring-enabled applications.

Example Thymeleaf Code:

Generated html

<table>
<thead>
<tr>
<th th:text="#{msgs.headers.name}">Name</th>
<th th:text="#{msgs.headers.price}">Price</th>
</tr>
</thead>
<tbody>
<tr th:each="prod : ${allProducts}">
<td th:text="${prod.name}">Oranges</td>
<td th:text="${#numbers.formatDecimal(prod.price, 1, 2)}">0.99</td>
</tr>
</tbody>
</table>

Html
●​ th:* attributes are processed by Thymeleaf on the server.
●​ th:text replaces the tag's body with a value.
●​ th:each iterates over a collection.
●​ ${...} is used to access model attributes.

Other famous template engines supported by Spring:

1.​ Jakarta Server Pages (JSP)


2.​ Jakarta Server Faces (JSF)
3.​ Apache FreeMarker
4.​ Groovy

For more details, please refer to https://www.thymeleaf.org/

SpringBoot DevTools
The Spring Boot DevTools provides features like Automatic restart & LiveReload that make the
application development experience a little more pleasant for developers. It can be added into any of
the SpringBoot projects by adding the below maven dependency.

Maven Dependency:

Generated xml

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>

Xml

Key Features of DevTools:

●​ Class Loaders: DevTools maintains 2 class loaders. One with classes that don't change
(third-party jars) and one with classes that change (your own code). When a restart is
needed, it only reloads the second class loader which makes restarts faster as well.
●​ LiveReload: DevTools includes an embedded LiveReload server that can be used to trigger
a browser refresh when a resource is changed. LiveReload related browser extensions are
freely available for Chrome, Firefox.
●​ Automatic Restart: DevTools triggers a restart when ever a build is triggered through an
IDE or by maven commands etc.
●​ Caching Disabled: DevTools disables caching options for template engines like Thymeleaf
by default during development.
●​ Packaged Archives: Repackaged archives do not contain DevTools by default. When you
build a final JAR/WAR, DevTools is excluded.
Chapter 11: Deeper into Spring MVC and Validation
Introduction to MVC Pattern
The Model-View-Controller (MVC) is a well-established architectural pattern that separates an
application into three main logical components. This separation makes it easier to manage
complexity, as each component has a distinct responsibility.

●​ Model: Represents the data of the application. This could be a single object (like a Person
with name and age) or a list of objects (like a list of students). It stores and manages the data
and is completely independent of the UI.
●​ View: Represents the UI. It takes data from the controller and displays it to the user, usually
with the help of HTML pages. The View's job is presentation; it should contain minimal logic.
●​ Controller: Acts as the brain of the MVC pattern. It controls the flow of the application. It
receives user input from the View, processes it (by interacting with the Model or other
services), and decides which business logic needs to be executed and which View to display
next.

The Model-View-Controller Design Pattern promotes a Separation of Concerns to achieve loose


coupling between the presentation layer (View) and the business logic (Model, Controller).

Spring MVC Architecture & Internal Flow


When a web request hits a Spring MVC application, it goes through a specific, well-defined internal
flow, orchestrated by the DispatcherServlet.
The Steps of a Request:

1.​ The Web Client makes an HTTP request to a URL (e.g., https://myapp.com/courses).
2.​ The Servlet Container (like the embedded Tomcat) accepts the HTTP request and hands
over the Servlet Request to the DispatcherServlet inside the Spring Web App.
3.​ The DispatcherServlet will check with the HandlerMapping to identify the controller
class and method name to invoke based on the HTTP method and path from the request.
The HandlerMapping acts like a registry of all @RequestMappings in the application.
4.​ The DispatcherServlet will invoke the corresponding Controller and method. After
execution, the controller will provide a view name (e.g., a string like "courses.html") and
any model data (e.g., a list of course objects) that needs to be rendered in the view.
5.​ The DispatcherServlet, with the help of a component called the ViewResolver, finds the
actual view (e.g., the courses.html Thymeleaf template) and renders it with the data
provided by the controller.
6.​ The Servlet Container (Tomcat) accepts the ServletResponse (which now contains the
rendered HTML) from the DispatcherServlet and converts the same to an HTTP
response before returning to the client.
7.​ The browser or client intercepts the HTTP response and displays the view, data, etc., to the
user.

Quick Tip: View Controllers


Do you know, we can register view controllers that create a direct mapping between the URL and the
view name using the ViewControllerRegistry? This way, there's no need for any Controller
between the two for simple static pages.

This is useful for pages that don't need any model data from a controller.

Example Configuration:

Generated java

@Configuration
public class WebConfig implements WebMvcConfigurer {

@Override
public void addViewControllers(ViewControllerRegistry registry) {
// This maps the URL "/courses" directly to the view named "courses"
registry.addViewController("/courses").setViewName("courses");

// This maps the URL "/about" directly to the view named "about"
registry.addViewController("/about").setViewName("about");
}
}

Java
Reducing Boilerplate Code with Lombok
Java expects a lot of boilerplate code inside POJO classes, like getters, setters, equals(),
hashCode(), and toString() methods.

●​ Lombok is a Java library that provides you with several annotations aimed at avoiding
writing Java code known to be repetitive and/or boilerplate.
●​ It can be added into any of the Java projects by adding the below maven dependency.

Maven Dependency:

Generated xml

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>

Xml

How Lombok Works:​


Project Lombok works by plugging into your build process. Then, it will auto-generate the Java
bytecode into your .class files required to implement the desired behavior, based on the
annotations you used. The source code remains clean and short, but the compiled code has all the
necessary methods.

Most Commonly Used Lombok Annotations:

●​ @Getter, @Setter
●​ @NoArgsConstructor, @RequiredArgsConstructor, @AllArgsConstructor
●​ @ToString, @EqualsAndHashCode
●​ @Data: This is a shortcut annotation that combines the features of @ToString,
@EqualsAndHashCode, @Getter, @Setter, and @RequiredArgsConstructor together.
Example with

The @Data annotation is provided by the Lombok library which generates getter, setter, equals(),
hashCode(), and toString() methods and a constructor at compile time. This makes our code
short and clean.

Source Code (

Generated java

@Data
public class Contact {
private String name;
private String mobileNum;
private String email;
private String subject;
private String message;
}

Java

A sample outline of a Java POJO class with @Data annotation used. The source code will not have
boilerplate code, but the compiled byte code will have it. When viewed in an IDE with the Lombok
plugin installed, you can often see the "outline" view showing all the generated methods.

@RequestParam Annotation
●​ In Spring, the @RequestParam annotation is used to map either query parameters or form
data from an HTTP request to a method parameter.
●​ For example, if we want to get a parameter's value from an HTTP GET requested URL like
http://localhost:8080/holidays?festival=true&federal=true, then we can use
the @RequestParam annotation as in the example below.

Example Usage:

Generated java

@GetMapping("/holidays")
public String displayHolidays(@RequestParam(required = false) boolean festival,
@RequestParam(required = false) boolean federal) {
// Business Logic
return "holidays.html";
}
Java

Key Attributes of

●​ name: The name attribute indicates the name of the request parameter to bind to.
●​ required: The required attribute is used to make a field either optional or mandatory. If it
is mandatory (true, which is the default) and the parameter is missing, an exception will be
thrown.
●​ defaultValue: The defaultValue attribute is used to handle missing values or null values.
If the parameter does not contain any value then this default value will be considered.
●​ value: The value attribute is similar to the name element and can be used as an alias.

@PathVariable Annotation
●​ The @PathVariable annotation is used to extract a value from the URI path itself. It is
most suitable for the RESTful web service where the URL contains some value.
●​ Spring MVC allows us to use multiple @PathVariable annotations in the same method.
●​ For example, if we want to get the value from a requested URI path, like
http://localhost:8080/holidays/all, http://localhost:8080/holidays/federal,
or http://localhost:8080/holidays/festival, then we can use the @PathVariable
annotation like in the example below.

Example Usage:

Generated java

@GetMapping("/holidays/{display}")
public String displayHolidays(@PathVariable String display) {
// Here, 'display' will contain "all", "federal", or "festival"
// Business Logic
return "holidays.html";
}

Java

Key Attributes of ​
The @PathVariable annotation supports attributes like name, required, and value, similar to
@RequestParam. We can use them in our application based on the requirements.

Validation with Spring Boot


●​ Bean Validation (https://beanvalidation.org/) is the standard for implementing
validations in the Java ecosystem. It's well integrated with Spring and Spring Boot.
●​ Below is the maven dependency that we can add to implement Bean validations in any
Spring/SpringBoot project.

Maven Dependency:

Generated xml

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Xml

How it Works:

●​ Defining Constraints: Bean Validation works by defining constraints to the fields of a class
by annotating them with certain annotations.
●​ Triggering Validation: We can put the @Valid annotation on method parameters and fields
to tell Spring that we want a method parameter or field to be validated.
●​ Validation Packages: Below are the important packages where validations-related
annotations can be identified.
○​ jakarta.validation.constraints.* (Standard JSR-380 annotations)
○​ org.hibernate.validator.constraints.* (Hibernate Validator specific
extensions)
●​

Important Validation Annotations


jakarta.validation.constraints.*

●​ @Digits: Must be a number within a specified range.


●​ @Email: Must be a well-formed email address.
●​ @Max, @Min: Must be a number less than/greater than or equal to the specified value.
●​ @NotBlank: The annotated element must not be null and must contain at least one
non-whitespace character.
●​ @NotEmpty: The annotated element must not be null nor empty.
●​ @NotNull: The annotated element must not be null.
●​ @Pattern: Must match the specified regular expression.
●​ @Size: The size of the element (e.g., String length, Collection size) must be between the
specified boundaries.

org.hibernate.validator.constraints.*

●​ @CreditCardNumber: Checks if the string is a valid credit card number.


●​ @Length: A size validation for strings.
●​ @Currency: Checks if the CharSequence is a valid currency.
●​ @Range: A numeric value is between the given boundaries.
●​ @URL: Checks for a valid URL.
●​ @UniqueElements: Checks if a collection contains unique elements only.
●​ @EAN, @ISBN: For barcodes and book numbers.

Validation in Practice
Step 1: Declare Validations in a POJO class.

Generated java

@Data
public class Contact {

@NotBlank(message="Name must not be blank")


@Size(min=3, message="Name must be at least 3 characters long")
private String name;

@NotBlank(message="Email must not be blank")


@Email(message = "Please provide a valid email address" )
private String email;

@NotBlank(message="Subject must not be blank")


@Size(min=5, message="Subject must be at least 5 characters long")
private String subject;

@NotBlank(message="Message must not be blank")


@Size(min=10, message="Message must be at least 10 characters long")
private String message;
}

Java

Step 2: Trigger Validation in the Controller.


We can put the @Valid annotation on method parameters to tell the Spring framework that we want
a particular POJO object's fields to be validated based on the validation annotation configurations.
For any issues, the framework populates the error details inside the Errors object. The errors can
be used to display on the UI to the user. Sample example code is below.

Generated java

@PostMapping(value = "/saveMsg")
public String saveMessage(@Valid @ModelAttribute("contact") Contact contact, Errors errors) {

if (errors.hasErrors()) {
log.error("Contact form validation failed due to : " + errors.toString());
return "contact.html"; // Return to the form page to display errors
}

contactService.saveMessageDetails(contact);
return "redirect:/contact";
}

Java

Step 3: Display Errors in the View (Thymeleaf).

Generated html

<ul>
<li class="alert alert-danger" role="alert" th:each="error : ${#fields.errors('contact.*')}"
th:text="${error}" />
</ul>

Html

This Thymeleaf snippet will iterate through all validation errors associated with the contact object
and display them in a list.
Chapter 12: Spring Web Scopes
In addition to the core Singleton and Prototype scopes, Spring provides three additional scopes that
are only available in the context of a web-aware ApplicationContext. These scopes tie the
lifecycle of a bean to the lifecycle of an HTTP request, session, or the entire web application.

The primary Bean Scopes inside Spring are:

1.​ Singleton
2.​ Prototype
3.​ Request
4.​ Session
5.​ Application

Defining Spring Web Scopes


These web-aware scopes are essential for managing state in web applications.

●​ Request Scope (: Spring creates an instance of the bean class for every single HTTP
request. The instance exists only for that specific HTTP request. Once the request is
complete and a response has been sent, the bean is destroyed.
●​ Session Scope (: Spring creates an instance and keeps the instance in the server's memory
for the full HTTP session. Spring links the instance in the context with the client's session.
Different users (with different sessions) will get different instances of the bean. The bean is
destroyed when the HTTP session is invalidated (e.g., due to timeout or logout).
●​ Application Scope (: The instance is unique in the app's context and it's available while the
app is running. Only one instance of the bean is created for the entire lifecycle of the
ServletContext. It's similar to a Singleton bean, but specifically tied to the web
application's lifecycle.

Key Points of Spring Web Scopes


Each web scope has specific characteristics and use cases.

Request Scope
●​ Lifecycle: Spring creates a lot of instances of this bean in the app's memory for each HTTP
request. So these types of beans are short-lived.
●​ Performance: Since Spring creates a lot of instances, please make sure to avoid
time-consuming logic while creating the instance.
●​ Use Cases: Can be considered for scenarios where the data needs to be reset after a new
request or page refresh, etc. For example, holding data that is only relevant for the single
request being processed.

Session Scope

●​ Lifecycle: Session-scoped beans have a longer life and they are less frequently garbage
collected compared to request-scoped beans.
●​ Performance & Security: Avoid keeping too much information inside session data as it
impacts performance (uses server memory) and can pose security risks. Never store
sensitive information as well.
●​ Use Cases: Can be considered for scenarios where the same data needs to be accessed
across multiple pages for a single user, like user information (e.g., name, preferences) or a
shopping cart.

Application Scope

●​ Lifecycle: In the application scope, Spring creates a bean instance per web application
runtime.
●​ Comparison to Singleton: It is similar to the singleton scope, with one major difference. A
Singleton scoped bean is singleton per ApplicationContext, whereas an application scoped
bean is singleton per ServletContext. In most Spring Boot applications, these are effectively
the same.
●​ Use Cases: Can be considered for scenarios where we want to store application-wide
data, like Drop Down values, Reference table values which won't change for all the users.
For example, a list of countries or application configuration settings that are loaded once at
startup.
Chapter 13: Securing Your Application with Spring
Security
Introduction to Spring Security
●​ Spring Security is a powerful and highly customizable authentication and access-control
framework. It is the de-facto standard for securing Spring-based applications.
●​ Below is the maven dependency that we can add to implement security using the Spring
Security project in any of the SpringBoot projects.

Maven Dependency:

Generated xml

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>

Xml

What Spring Security Provides:

●​ Comprehensive Protection: Spring Security is a framework that provides authentication,


authorization, and protection against common attacks.
●​ Ease of Use: Spring Security helps developers with easier configurations to secure a web
application by using a standard username/password authentication mechanism.
●​ Out-of-the-Box Features: Spring Security provides out-of-the-box features to handle
common security attacks like CSRF and CORS. It also has good integration with security
standards like JWT, OAUTH2, etc.

Authentication & Authorization


These two terms are the foundation of security, but they mean different things.
AUTHENTICATION AUTHORIZATION

In authentication, the identity of users is In authorization, a person's or user's


checked for providing access to the system. authorities are checked for accessing
(Are you who you say you are?) the resources. (Are you allowed to do
this?)

Authentication is done before authorization. Authorization always happens after


authentication.

It needs the user's login details (e.g., It needs the user's privileges or roles.
username, password, token).

If authentication fails, we usually get a 401 If authorization fails, we usually get a 403
Unauthorized error response. Forbidden error response.

For example, as a Bank customer/employee, Once logged into the application, my


in order to perform actions in the app, we roles and authorities will decide what
need to prove our identity. kind of actions I can do.

Quick Tip: Default Security


Do you know, as soon as we add the spring-security-dependency to a web application, by
default it protects all the pages/APIs inside it?

●​ It will redirect to the inbuilt login page to enter credentials.


●​ The default credentials are: username is user and the password is randomly generated
and printed on the console on startup.
●​ We can configure custom credentials using the below properties to get started for POCs etc.
But for PROD applications, Spring Security supports user credentials configuration inside a
DB, LDAP, OAUTH2 Server, etc.
●​ Generated properties

spring.security.user.name = eazybytes

●​ spring.security.user.password = 12345

Properties
Default Security Configurations in Spring Security
By default, the Spring Security framework protects all the paths present inside the web application.
This behavior is due to the code present inside the method
defaultSecurityFilterChain(HttpSecurity http) of the class
SpringBootWebSecurityConfiguration.

The key part of the default configuration looks like this:

Generated java

@Bean
@Order(SecurityProperties.BASIC_AUTH_ORDER)
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> requests.anyRequest().authenticated()); // All requests must
be authenticated
http.formLogin(withDefaults()); // Enable form-based login
http.httpBasic(withDefaults()); // Enable HTTP Basic authentication
return http.build();
}

Java

●​ anyRequest().authenticated(): This is the crucial line. It enforces that any request to


the application must come from an authenticated user.

Configure permitAll() with Spring Security


●​ Using permitAll() configurations, we can allow full/public access to a specific
resource/path or all the resources/paths inside a web application without security.
●​ Below is the sample configuration that we can do in order to allow any requests in a Web
application without security.

Generated java

@Configuration
public class ProjectSecurityConfig {

@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> requests.anyRequest().permitAll()); // Allow all requests
http.formLogin(Customizer.withDefaults());
http.httpBasic(Customizer.withDefaults());
return http.build();
}
}

Java

Key Features:

●​ Form Login provides support for username and password being provided through an HTML
form.
●​ HTTP Basic Auth uses an HTTP header in order to provide the username and password
when making a request to a server.

Configure denyAll() with Spring Security


●​ Using denyAll() configurations, we can deny access to a specific resource/path or all the
resources/paths inside a web application, regardless of user authentication.
●​ Below is the sample configuration that we can do in order to deny any requests that are
coming into a web application.

Generated java

@Configuration
public class ProjectSecurityConfig {

@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((requests) -> requests.anyRequest().denyAll()); // Deny all requests
http.formLogin(Customizer.withDefaults());
http.httpBasic(Customizer.withDefaults());
return http.build();
}
}

Java

Use Cases:

●​ denyAll() is usually used to retire a specific API temporarily without removing the code.
●​ permitAll() is used to allow public access to public APIs, paths, CSS, images, JS files,
etc.
Configure Custom Security Configs & CSRF Disable
We can apply custom security configurations based on our requirements for each API/URL like
below.

●​ permitAll() can be used to allow access without security and authenticated() can be
used to protect a web page/API.
●​ By default, any requests with HTTP methods that can update data like POST, PUT will be
stopped with a 403 error due to CSRF protection. We can disable the same for now and
enable it in the coming sections when we start generating CSRF tokens.
●​ Below is the sample configuration that we can do to implement custom security configs and
disable CSRF.

Generated java

@Configuration
public class ProjectSecurityConfig {

@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable() // Disable CSRF protection for now
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/", "/home").permitAll()
.requestMatchers("/holidays/**").permitAll()
.requestMatchers("/contact").permitAll()
.requestMatchers("/saveMsg").permitAll()
.requestMatchers("/courses").permitAll()
.requestMatchers("/about").permitAll()
.requestMatchers("/assets/**").permitAll()
.requestMatchers("/dashboard").authenticated() // This path is protected
.anyRequest().denyAll()); // Deny any other request
http.formLogin(Customizer.withDefaults());
http.httpBasic(Customizer.withDefaults());
return http.build();
}
}

Java

In-Memory Authentication in Spring Security


●​ Spring Security provides support for username/password-based authentication based on
users stored in application memory.
●​ Like mentioned below, we can configure any number of users, their roles, and passwords
using in-memory authentication.

Generated java

@Configuration
public class ProjectSecurityConfig {

// ... other security chain configuration ...

@Bean
public InMemoryUserDetailsManager userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder()
.username("user")
.password("12345")
.roles("USER")
.build();
UserDetails admin = User.withDefaultPasswordEncoder()
.username("admin")
.password("54321")
.roles("USER", "ADMIN")
.build();
return new InMemoryUserDetailsManager(user, admin);
}
}

Java

Important Note: In-memory authentication is ideal for POC Web Apps or any internal Web Apps
that get used only in non-prod environments. NEVER EVER use in-memory authentication for
PROD web applications.

Configuring Login & Logout Page


●​ Spring Security allows us to configure a custom login page to our web application instead
of using the Spring Security default provided login page.
●​ Similarly, we can configure the logout page as well.
●​ Below is the sample configuration that we can follow.

Generated java

@Configuration
public class ProjectSecurityConfig {

@Bean
SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception {
http.csrf().disable()
.authorizeHttpRequests((requests) -> requests
.requestMatchers("/dashboard").authenticated()
.requestMatchers("/", "/home").permitAll()
// ... other matchers ...
.requestMatchers("/login").permitAll() // Allow access to the custom login page
.requestMatchers("/logout").permitAll() // Allow access to logout
.anyRequest().denyAll())
.formLogin(loginConfigurer -> loginConfigurer
.loginPage("/login") // Custom login page URL
.defaultSuccessUrl("/dashboard") // Redirect on success
.failureUrl("/login?error=true").permitAll()) // Redirect on failure
.logout(logoutConfigurer -> logoutConfigurer
.logoutSuccessUrl("/login?logout=true") // Redirect after logout
.invalidateHttpSession(true).permitAll());
http.httpBasic(Customizer.withDefaults());
return http.build();
}
// ... user details service bean ...
}

Java

Note: A configured login page will be shown if the user tries to access a secured page/resource
without a valid authenticated session. The same behavior applies for the default login page provided
by Spring Security.

Chapter 14: Advanced Topics in Spring MVC & Security


Quick Tip: Thymeleaf & Spring Security Integration
Do you know, Thymeleaf has a great integration with Spring Security? More details can be found at
https://www.thymeleaf.org/doc/articles/springsecurity.html.

This integration allows you to conditionally render parts of your HTML view based on the user's
authentication status and roles directly within your templates.

Step 1: Add the below dependency in the

This brings in the necessary integration module.

Generated xml

<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
<!-- Use springsecurity5 for Spring Boot 2.x -->
</dependency>

Xml

Step 2: Add the below XML name space which enables us to use Thymeleaf Security related
tags.

Add this to your root <html> tag in your templates.

Generated html

<html lang="en" xmlns:th="http://www.thymeleaf.org"


xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity6">

Html

Step 3: Use any of the Thymeleaf Security tags inside the HTML code based on
Authentication details.

These special attributes allow you to show/hide content dynamically.

●​ sec:authorize="isAnonymous()": Content is shown only to users who are not logged in.
●​ sec:authorize="isAuthenticated()": Content is shown only to logged-in users.
●​ sec:authorize="hasRole('ROLE_ADMIN')": Content is shown only to users with the
'ADMIN' role.
●​ sec:authentication="name": Displays the username of the logged-in user.
●​ sec:authentication="principal.authorities": Displays the authorities/roles of the
logged-in user.

Example Usage in a Navigation Bar:

Generated html

<nav>
<!-- Show Login link only if user is not authenticated -->
<a href="/login" sec:authorize="isAnonymous()">Login</a>

<!-- Show Dashboard and Logout links only if user is authenticated -->
<div sec:authorize="isAuthenticated()">
Welcome, <span sec:authentication="name">User</span>!
<a href="/dashboard">Dashboard</a>
<a href="/logout">Logout</a>
</div>

<!-- Show Admin Panel link only to admins -->


<a href="/admin" sec:authorize="hasRole('ROLE_ADMIN')">Admin Panel</a>
</nav>

Html

@ControllerAdvice & @ExceptionHandler for Global Exception Handling


●​ @ControllerAdvice is a specialization of the @Component annotation which allows you to
handle exceptions across the whole application in one global handling component. You can
think of it as an interceptor of exceptions thrown by methods annotated with
@RequestMapping or one of the shortcuts like @GetMapping, etc.
●​ We can define the exception handling logic inside a method and annotate it with
@ExceptionHandler.
●​ Below is the sample configuration that we can follow.
Example of a Global Exception Handler:

Generated java

@ControllerAdvice
public class GlobalExceptionController {

@ExceptionHandler(Exception.class)
public ModelAndView exceptionHandler(Exception exception) {
ModelAndView errorPage = new ModelAndView();
errorPage.setViewName("error"); // The name of the error view (e.g., error.html)
errorPage.addObject("errormsg", exception.getMessage());
return errorPage;
}
}

Java

How it Works:​
The combination of @ControllerAdvice and @ExceptionHandler can handle exceptions across
all the controllers inside a web application globally. When any controller throws an Exception (or a
subclass of it), this exceptionHandler method will be invoked. It then prepares a ModelAndView
object, setting the view to error.html and adding the exception message to the model so it can be
displayed on the page.

Quick Tip: Fine-Grained Exception Handling


Do you know, there are more ways to use @ExceptionHandler?

●​ If a method annotated with @ExceptionHandler is present inside a @Controller class,


then the exception handling logic will be applicable for any exceptions that occurred in that
specific controller class only. This allows for controller-specific error handling.
●​ If the same @ExceptionHandler annotated method is present inside a
@ControllerAdvice class, then the exception handling logic will be applicable for any
exceptions that occurred across all the controller classes.
●​ Using the @ExceptionHandler annotation, we can handle any number of exceptions like
the below sample code. You can list multiple exception classes in the annotation's value.
Example Handling Multiple Exceptions:

Generated java

@ExceptionHandler({NullPointerException.class,
ArrayIndexOutOfBoundsException.class,
IOException.class})
public ModelAndView handleSpecificExceptions(RuntimeException exception) {
// ... custom exception handling logic ...
// You might want to return different error pages or messages
// based on the type of exception.
return new ModelAndView("error_specific.html");
}

Java

Cross-Site Request Forgery (CSRF)


●​ A typical Cross-Site Request Forgery (CSRF or XSRF) attack aims to perform an operation
in a web application on behalf of a user without their explicit consent. In general, it doesn't
directly steal the user's identity, but it exploits the user to carry out an action without
their will.
●​ Consider you are a user using a website netflix.com and the attacker's website evil.com.

The Attack Flow:

Step 1: The Netflix user logs in to Netflix.com. The backend server of Netflix will provide a cookie
which will be stored in the browser against the domain name Netflix.com.

Step 2: The same Netflix user opens an evil.com website in another tab of the browser. The user
accesses an evil blog/site hosted on evil.com. evil.com returns a web page which has an
embedded malicious link to change the email of the Netflix account. The link appears with a text like
"90% OFF on iPhone".
Step 3: The user is tempted and clicks on the malicious link, which makes a POST request to
Netflix.com. And since the login cookie is already present in the same browser and the request to
change the email is being made to the same domain Netflix.com, the backend server of
Netflix.com can't differentiate from where the request came. So here, the evil.com website
forged the request as if it is coming from a Netflix.com UI page.

Example of the Malicious Code on

Generated html

<!-- This form is hidden and submits automatically -->


<form action="https://netflix.com/changeEmail" method="POST" id="form">
<input type="hidden" name="email" value="[email protected]">
</form>
<script>
document.getElementById('form').submit();
</script>
<!-- The user clicks a link, and "Boom !! The email of the Netflix account changed" -->

Html

Solution to CSRF
●​ To defeat a CSRF attack, applications need a way to determine if the HTTP request is
legitimately generated via the application's user interface. The best way to achieve this is
through a CSRF token.
●​ A CSRF token is a secure random token that is used to prevent CSRF attacks. The token
needs to be unique per user session and should be of a large random value to make it
difficult to guess.
●​ Let's see how this solves the CSRF attack by taking the previous Netflix example again.

The Protected Flow:


Step 1: The Netflix user logs in to Netflix.com. The backend server of Netflix will provide a cookie
which will be stored in the browser against the domain name Netflix.com along with a randomly
generated unique CSRF token for this particular user session. The CSRF token is inserted
within hidden parameters of HTML forms to avoid exposure to session cookies.

Step 2: The same Netflix user opens an evil.com website in another tab of the browser and clicks
the malicious link.

Step 3: The user is tempted and clicks on the malicious link which makes a request to
Netflix.com. And since the login cookie is already present in the same browser, the request to
change email is being made to the same domain Netflix.com. But this time, the Netflix.com
backend server expects the CSRF token along with the cookie. The CSRF token must be the same
as the initial value generated during the login operation. The request from evil.com does not have
this token.

●​ Boom !! The Netflix server threw an error 403.

The CSRF token will be used by the application server to verify the legitimacy of the end-user
request if it is coming from the same App UI or not. The application server rejects the request if the
CSRF token fails to match the test.

Quick Tip: CSRF in Spring Security


●​ Do you know, by default, Spring Security enables CSRF fix for all the HTTP methods
which result in data change like POST, DELETE, etc. But not for GET. This is why our POST
requests were failing earlier.
●​ Using Spring Security configurations, we can disable the CSRF protection for the
complete application or for only a few paths based on our requirements like below.
○​ http.csrf((csrf) -> csrf.disable())
○​ http.csrf((csrf) -> csrf.ignoringRequestMatchers("/saveMsg"))
●​
●​ Thymeleaf has great integration & support with Spring Security to generate a CSRF token.
We just need to add the below code in the login HTML form code, and Thymeleaf will
automatically append the CSRF token for the remaining pages/forms inside the web
application.
●​ Generated html

<input type="hidden" th:name="${_csrf.parameterName}" th:value="${_csrf.token}" />


Html

Chapter 15: Database Integration with Spring Boot


Spring Boot & H2 Database
●​ H2 is an embedded, open-source, in-memory database. SpringBoot supports integration
with H2 DB which can be used for POC applications and is excellent for examples/testing.
●​ Below is the maven dependency that we can add to any SpringBoot project in order to use
the internal memory H2 Database.

Maven Dependency:

Generated xml

<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>

Xml

Key Configuration Points:

●​ Schema and Data: Since it is an internal memory DB, we need to create the schema and
data that is needed during the startup of the App. Any updates to the data will be lost after
restarting the server.
●​ SQL Scripts: To create schema & data for the H2 DB, we can add schema.sql and
data.sql inside the maven project's resources folder. Any table creation scripts and DB
records scripts can be present inside schema.sql and data.sql respectively.
●​ H2 Console: By default, the H2 web console is available at /h2-console. You can
customize the console's path by using the spring.h2.console.path property.
●​ Credentials: The default credentials of H2 DB are username sa and password "" (Empty).
Chapter 16: Database Connectivity with JDBC and Spring
JDBC
Intro to JDBC & Problems With It
Before frameworks like Spring simplified database access, developers used JDBC (Java Database
Connectivity) directly.

Key Points of JDBC:

●​ Intro to JDBC:
○​ JDBC or Java Database Connectivity is a specification from Core Java that
provides a standard abstraction for Java apps to communicate with various
databases.
○​ A JDBC API along with the database driver is capable of accessing databases.
○​ JDBC is a base framework or standard for frameworks like Hibernate, Spring Data
JPA, MyBatis, etc.

●​ Steps in JDBC to access DB:​


We need to follow the below steps to access a DB using JDBC:
○​ Load Driver Class
○​ Obtain a DB connection
○​ Obtain a statement using the connection object
○​ Execute the query
○​ Process the result set
○​ Close the connection (and statement, and result set)
●​
●​ Problem with JDBC:
○​ Developers are forced to follow all the steps mentioned to perform any kind of
operation with the DB, which results in a lot of duplicate code at many places.
○​ Developers need to handle the checked exceptions that will be thrown from the
API. This leads to verbose try-catch-finally blocks.
○​ JDBC is database dependent in some areas, especially with SQL syntax variations.
Sample Program using JDBC
The following sample program uses JDBC to fetch a record from the database table. You can see
there is a lot of boilerplate code present.

Generated java

public Optional<Contact> findById(int id) {


Connection connection = null;
PreparedStatement statement = null;
ResultSet resultSet = null;
Contact contact = null;

try {
Class.forName("com.mysql.jdbc.Driver"); // Step 1
connection = DriverManager.getConnection("url", "username", "pwd"); // Step 2
statement = connection.prepareStatement("select * from contact where id=?"); // Step 3
statement.setInt(1, id);
resultSet = statement.executeQuery(); // Step 4

if (resultSet.next()) { // Step 5
contact = new Contact();
contact.setId(resultSet.getInt("id"));
contact.setName(resultSet.getString("name"));
contact.setMessage(resultSet.getString("message"));
}
} catch (SQLException e) {
// ??? What should be done here ???
} catch (ClassNotFoundException e) {
// Handle exception
} finally { // Step 6 - very verbose
if (resultSet != null) {
try { resultSet.close(); } catch (SQLException e) {}
}
if (statement != null) {
try { statement.close(); } catch (SQLException e) {}
}
if (connection != null) {
try { connection.close(); } catch (SQLException e) {}
}
}

return Optional.ofNullable(contact);
}

Java
This demonstrates the verbosity, especially the resource cleanup in the finally block and the need
for multiple catch blocks.

Intro to Spring JDBC


●​ Spring JDBC simplifies the use of JDBC and helps to avoid common errors. It executes the
core JDBC workflow, leaving application code to provide SQL and extract results.
●​ It does this magic by providing JDBC templates which developers can use inside their
applications.
●​ Below is the maven dependency that we need to add to any Spring/SpringBoot project in
order to use Spring JDBC-provided templates.

Maven Dependency:

Generated xml

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

Xml

Key Features of Spring JDBC Templates:

●​ Templates for Common Operations: Spring provides many templates for JDBC-related
activities. Among them, the famous ones are JdbcTemplate and
NamedParameterJdbcTemplate.
●​ Classic and Low-Level Approach: JdbcTemplate is the classic and most popular Spring
JDBC approach. It provides a "lowest-level" approach, and all other templates use
JdbcTemplate under the covers.
●​ Named Parameters: NamedParameterJdbcTemplate wraps a JdbcTemplate to provide
named parameters instead of the traditional JDBC ? placeholders. This approach provides
better documentation and ease of use when you have multiple parameters for an SQL
statement.
Spring JDBC - Who Does What?
This table clearly shows how Spring JDBC divides the responsibilities, simplifying the developer's
job.

Action Spring Developer


JDBC

Define connection parameters ✔️


Open the connection ✔️
Specify the SQL statement ✔️
Declare parameters and provide parameter values ✔️
Prepare and run the statement ✔️
Set up the loop to iterate through the results (if any) ✔️
Do the work for each iteration ✔️
Process any exception ✔️
Handle transactions ✔️
Close the connection, the statement, and the resultset ✔️
As you can see, Spring handles all the tedious and error-prone parts like opening/closing resources
and exception handling.

Using JdbcTemplate
●​ JdbcTemplate is the central class in the JDBC core package. It handles the creation and
release of resources, which helps you avoid common errors, such as forgetting to close the
connection. It performs the basic tasks of the core JDBC workflow (such as statement
creation and execution), leaving application code to provide SQL and extract results.
●​ You can use JdbcTemplate within a DAO implementation through direct instantiation with a
DataSource reference, or you can configure it in a Spring IoC container and give it to DAOs
as a bean reference.
●​ We need to follow the below steps in order to configure JdbcTemplate inside a Spring Web
application (without Spring Boot auto-configuration).

Step 1: First, we need to create a

Generated java

@Configuration
public class ProjectConfig {
@Bean
public DataSource myDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/eazyschool");
dataSource.setUsername("user");
dataSource.setPassword("password");
return dataSource;
}
}

Java

Step 2: Inside any Repository/DAO classes where we want to execute queries, we need to
create a bean/object of

Generated java

@Repository
public class PersonDAOImpl implements PersonDAO {

private final JdbcTemplate jdbcTemplate;

@Autowired
public PersonDAOImpl(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}

// ... methods using jdbcTemplate ...


}

Java
●​ Important: Instances of the JdbcTemplate class are thread-safe once configured. This is
important because it means we can configure a single instance of a JdbcTemplate and then
safely inject this shared reference into multiple DAOs (or repositories).

JdbcTemplate in Action: Query Examples


Sample usage of

●​ The following query gets the number of rows in a table.


●​ Generated java

int rowCount = this.jdbcTemplate.queryForObject("select count(*) from person", Integer.class);

Java

●​ The following query uses a bind variable.

Generated java

int countOfPersonsNamedJoe = this.jdbcTemplate.queryForObject(

"select count(*) from person where first_name = ?", Integer.class, "Joe");

Java

●​ The following query looks for a string column based on a condition.

Generated java

String lastName = this.jdbcTemplate.queryForObject(

"select last_name from person where id = ?", String.class, 1212L);


Java

Sample usage of

●​ The following example inserts a new entry.


●​ Generated java

this.jdbcTemplate.update("insert into person (first_name, last_name) values (?, ?)",

"Jon", "Doe");

Java

●​ The following example updates an existing entry.

Generated java

this.jdbcTemplate.update("update person set last_name = ? where id = ?",

"Maria", 5276L);
Java

●​ The following example deletes an entry.

Generated java

this.jdbcTemplate.update("delete from person where id = ?", 5276L);

Java

Other

●​ You can use the execute(..) method to run any arbitrary SQL. Consequently, the method
is often used for DDL statements.

Generated java

this.jdbcTemplate.execute("create table person (id integer, name varchar(100))");

Java

●​ The following example invokes a stored procedure.

Generated java

this.jdbcTemplate.update("call SUPPORT.REFRESH_PERSON_SUMMARY(?)", 5276L);

Java
Using RowMapper
What if our query returns a complex object, not just a single value? This is where RowMapper comes
in.

●​ RowMapper interface allows us to map a row of the relations with the instance of a
user-defined class. It iterates the ResultSet internally and adds the data into the collection.
So we don't need to write a lot of code to fetch the records as ResultSetExtractor.
●​ RowMapper saves a lot of code because it internally adds the data of ResultSet into the
collection.
●​ It defines only one method mapRow that accepts a ResultSet instance and int as
parameters. Below is the sample usage of it.

Example

Generated java

private final RowMapper<Person> personRowMapper = (resultSet, rowNum) -> {


Person person = new Person();
person.setFirstName(resultSet.getString("first_name"));
person.setLastName(resultSet.getString("last_name"));
return person;
};

public List<Person> findAllPersons() {


return this.jdbcTemplate.query("select first_name, last_name from person", personRowMapper);
}

Java

Quick Tip: BeanPropertyRowMapper


Do you know, if the column names in a table and field names inside a POJO/Bean are
matching, then we can use BeanPropertyRowMapper which is provided by the Spring framework?

Spring's BeanPropertyRowMapper class saves you a lot of time since we don't have to define the
mappings like we do inside a RowMapper implementation. It automatically maps columns to bean
properties based on matching names.

Example Usage:
Generated java

// Assumes HOLIDAYS table has columns that match the fields in the Holiday class
public List<Holiday> findAllHolidays() {
String sql = "SELECT * FROM HOLIDAYS";
var rowMapper = BeanPropertyRowMapper.newInstance(Holiday.class);
return jdbcTemplate.query(sql, rowMapper);
}

Java

Using NamedParameterJdbcTemplate
●​ The NamedParameterJdbcTemplate class adds support for programming JDBC statements
by using named parameters, as opposed to programming JDBC statements using only
classic placeholder (?) arguments. The NamedParameterJdbcTemplate class wraps a
JdbcTemplate and delegates to the wrapped JdbcTemplate to do much of its work.
●​ The following example shows how to use NamedParameterJdbcTemplate.

Example Usage:

Generated java

// In your DAO/Repository
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;

public void setDataSource(DataSource dataSource) {


this.namedParameterJdbcTemplate = new NamedParameterJdbcTemplate(dataSource);
}

public int countOfPersonsByFirstName(String firstName) {


String sql = "select count(*) from Person where first_name = :first_name";
SqlParameterSource namedParameters = new MapSqlParameterSource("first_name", firstName);
return this.namedParameterJdbcTemplate.queryForObject(sql, namedParameters, Integer.class);
}

Java

This is much more readable than using ? when you have many parameters.

Quick Tip: Spring Boot and JdbcTemplate


Do you know, working with JdbcTemplate in Spring Boot is very easy?

●​ Spring Boot auto-configures DataSource, JdbcTemplate, and


NamedParameterJdbcTemplate classes based on the DB connection details mentioned in
the application.properties file. You can @Autowire them directly into your own
repository classes, as shown in the following example.
●​ You can customize some properties of the template by using the spring.jdbc.template.*
properties, like mentioned below.​
spring.jdbc.template.max-rows=500

Example Repository in Spring Boot:

Generated java

@Repository
public class HolidaysRepository {

private final JdbcTemplate jdbcTemplate;

@Autowired
public HolidaysRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}

// ... methods using the autowired jdbcTemplate ...


}

Java

No manual DataSource or JdbcTemplate bean creation is needed!


Chapter 17: Simplifying Persistence with Spring Data
While Spring JDBC greatly reduces the boilerplate of raw JDBC, we still have to write SQL queries
manually. For most standard CRUD (Create, Read, Update, Delete) operations, even this can
become repetitive. This is where Spring Data comes in.

Intro to Spring Data


Spring Data is a Spring ecosystem project that simplifies the persistence layer's development
by providing implementations according to the persistence technology we use. This way, we only
need to write a few lines of code to define the repositories of our Spring app.

It sits as a layer of abstraction between your application logic and the underlying persistence
technology.

●​ High-Level Abstraction: Spring Data is a high-level layer that simplifies the persistence
implementation by unifying the various technologies under the same abstractions. Whether
you are using JPA/Hibernate, MongoDB, Cassandra, or another technology, you interact with
it through a consistent set of Spring Data interfaces.

Spring Data Repository Abstraction


●​ Whichever persistence technology your app uses, Spring Data provides a common set of
interfaces (contracts) you extend to define the app's persistence capabilities.
●​ The central interface in the Spring Data repository abstraction is Repository.

Key Repository Interfaces:

●​ : This is the most abstract contract. If you extend this contract, your app recognizes the
interface you write as a particular Spring Data repository. Still, you won't inherit any
predefined operations (such as adding a new record, retrieving all the records, or getting a
record by its primary key). The Repository interface doesn't declare any method (it is a
marker interface).
●​ : This is the simplest Spring Data contract that also provides some persistence capabilities. If
you extend this contract to define your app's persistence capabilities, you get the simplest
operations for creating, retrieving, updating, and deleting records. ListCrudRepository is
an extension to CrudRepository returning List instead of Iterable where ever
applicable.
●​ : This interface provides methods to retrieve entities using pagination and sorting
abstraction. ListPagingAndSortingRepository is an extension to
PagingAndSortingRepository returning List instead of Iterable where ever applicable.

Spring Data Repository Hierarchy


To implement your app's repositories using Spring Data, you extend specific interfaces. The main
interfaces that represent Spring Data contracts are Repository, CrudRepository,
ListCrudRepository, PagingAndSortingRepository, and ListPagingAndSortingRepository.
You extend one of these contracts to implement your app's persistence capabilities.

The hierarchy is as follows:

●​ ListCrudRepository extends CrudRepository.


●​ CrudRepository extends Repository.
●​ ListPagingAndSortingRepository extends PagingAndSortingRepository.
●​ PagingAndSortingRepository extends CrudRepository.

This layered approach allows you to choose exactly the level of functionality you need for your
repository.

Quick Tip: @Repository vs. Repository


Do you know there's a difference between the annotation and the interface?

●​ We should not confuse the @Repository annotation and the Spring Data Repository
interface. The @Repository annotation is a stereotype used to mark a class as a data
access component, while the Repository interface is a marker interface that is the
foundation of the Spring Data repository abstraction.
●​ Spring Data provides multiple interfaces that extend one another by following the principle
called interface segregation. This helps apps to extend what they want instead of always
following a fat implementation.
●​ Some Spring Data modules might provide specific contracts to the technology they
represent. For example, using Spring Data JPA, you can also extend the JpaRepository
interface directly. Similarly, using the Spring Data Mongo module, your app provides a
particular contract named MongoRepository.

Technology-Specific Repositories
The core repository interfaces are generic. Specific Spring Data modules (like Spring Data JPA or
Spring Data MongoDB) provide their own specialized repository interfaces that extend the core ones
and add technology-specific functionality.

●​ If you use Hibernate (which implements the JPA specification), you could extend the
JpaRepository contract, which adds JPA-specific methods that are useful for JPA usage.
●​ If your app uses MongoDB, you could define your Spring Data repository interface, which is
particular for Spring Data Mongo, by extending MongoRepository.

These technology-specific repositories still ultimately inherit from the core interfaces like
CrudRepository, providing a consistent programming model.

Intro to Spring Data JPA


Spring Data JPA is available to Spring Boot applications with the JPA starter. This starter
dependency not only brings in Spring Data JPA but also transitively includes Hibernate as the JPA
implementation.

Maven Dependency:

Generated xml

<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

Xml

●​ What is JPA? JPA is just a specification that defines an object-relational mapping (ORM)
standard for storing, accessing, and managing Java objects in a relational database.
Hibernate is the most popular and widely used implementation of the JPA specifications. By
default, Spring Data JPA uses Hibernate as its JPA provider.

We need to follow the below steps in order to query a DB using Spring Data JPA inside a Spring
Boot application.
Step 1: We need to indicate a Java POJO class as an entity class by using annotations like

This maps your Java object to a database table.

Generated java

@Entity
@Table(name="contact_msg")
public class Contact extends BaseEntity {

@Id
@GeneratedValue(strategy= GenerationType.AUTO,generator="native")
@GenericGenerator(name = "native",strategy = "native")
@Column(name = "contact_id")
private int contactId;

// ... other fields with @Column annotations ...


}

Java

Step 2: We need to create interfaces for a given table entity by extending framework-provided
Repository interfaces. This helps us to run the basic CRUD operations on the table without
writing method implementations.

You just define the interface; Spring Data provides the implementation at runtime!

Generated java

@Repository
public interface ContactRepository extends CrudRepository<Contact, Integer> {
// No implementation needed!
}

Java

Step 3: Enable JPA functionality and scanning by using the annotations


In a Spring Boot application with the starter, this is often done automatically, but you can configure it
explicitly if needed.

Generated java

@SpringBootApplication
@EnableJpaRepositories("com.eazybytes.eazyschool.repository")
@EntityScan("com.eazybytes.eazyschool.model")
public class EazyschoolApplication {
// ... main method ...
}

Java

Step 4: We can inject repository beans into any controller/service classes and execute the
required DB operations.

Generated java

@Service
public class ContactService {

@Autowired
private ContactRepository contactRepository;

public boolean saveMessageDetails(Contact contact) {


boolean isSaved = false;
Contact savedContact = contactRepository.save(contact); // Magic!
if (null != savedContact && savedContact.getContactId() > 0) {
isSaved = true;
}
return isSaved;
}
}

Java
The save() method is provided for free by extending CrudRepository.

Derived Query Methods in Spring Data JPA


This is one of the most powerful features of Spring Data.

●​ With Spring Data JPA, we can use the method names to derive a query and fetch the
results without writing code manually like with traditional JDBC.
●​ As a developer, we just need to define the query methods in a repository interface that
extends one of the Spring Data's repositories such as CrudRepository. Spring Data JPA
will create queries and implementations at runtime automatically by parsing these method
names.
●​ Below are a few examples.

Example Derived Queries:

Generated java

public interface PersonRepository extends CrudRepository<Person, Long> {

// find persons by last name


List<Person> findByLastName(String lastName);

// find person by email


Person findByEmail(String email);

// find person by email and last name


Person findByEmailAndLastName(String email, String lastname);
}

Java

You only define the method signature. Spring Data parses the method name, understands you want
to query based on the email and lastName fields, and generates the SELECT ... WHERE email =
? AND lastname = ? query for you.

Quick Tip: Anatomy of a Derived Query


Do you know a derived query method name has two main components separated by the first By
keyword?

1.​ The introducer clause like find, read, query, count, or get which tells Spring Data JPA
what you want to do with the method. This clause can contain further expressions, such as
Distinct to set a distinct flag on the query to be created.
2.​ The criteria clause that starts after the first By keyword. The first By acts as a delimiter to
indicate the start of the actual query criteria. The criteria clause is where you define
conditions on entity properties and concatenate them with And and Or keywords.

Using readBy, getBy, and queryBy in place of findBy will behave the same. For
example, readByEmail(String email) is same as findByEmail(String email).

Supported Keywords inside Method Names


Spring Data supports a rich set of keywords to build derived queries.

Keyword Sample method name Sample Query


that forms by JPA

Distinct findDistinctByLastnameAndFirstnam ... where


e x.lastname = ?1
and x.firstname
= ?2 (with select
distinct)

And findByLastnameAndFirstname ... where


x.lastname = ?1
and x.firstname
= ?2

Or findByLastnameOrFirstname ... where


x.lastname = ?1
or x.firstname
= ?2

Is, Equals findByFirstname, ... where


findByFirstnameIs, x.firstname =

findByFirstnameEquals ?1
Between findByStartDateBetween ... where
x.startDate
between ?1 and
?2

LessThan, findByAgeLessThan, ... where x.age


LessThanEqual findByAgeLessThanEqual < ?1, ... where
x.age <= ?1

GreaterThan, findByAgeGreaterThan, ... where x.age


GreaterThanEqual findByAgeGreaterThanEqual > ?1, ... where
x.age >= ?1

After, Before findByStartDateAfter, ... where


findByStartDateBefore x.startDate >
?1, ... where
x.startDate <
?1

IsNull, NotNull findByAgeIsNull, ... where x.age


(IsNotNull) findByAge(Is)NotNull is null, ...
where x.age not
null

Like, NotLike findByFirstnameLike, ... where


findByFirstnameNotLike x.firstname
like ?1, ...
where
x.firstname not
like ?1

| StartingWith, EndingWith, Containing | findByFirstnameStartingWith,


findByFirstnameEndingWith, findByFirstnameContaining | ... where x.firstname like
?1 (param bound with %) |​
| OrderBy | findByAgeOrderByLastnameDesc | ... where x.age = ?1 order by x.lastname
desc |​
| Not | findByLastnameNot | ... where x.lastname <> ?1 |​
| In, NotIn | findByAgeIn(Collection<Age> ages), findByAgeNotIn(...) | ... where x.age
in ?1, ... where x.age not in ?1 |​
| True, False | findByActiveTrue(), findByActiveFalse() | ... where x.active = true,
... where x.active = false |​
| IgnoreCase | findByFirstnameIgnoreCase | ... where UPPER(x.firstname) = UPPER(?1) |

Spring Data JPA is a powerful tool that provides an extra layer of abstraction on top of an existing
JPA provider like Hibernate. The derived query feature is one of the most loved features of Spring
Data JPA.

Chapter 18: Advanced Spring Data JPA Features


Beyond derived queries, Spring Data JPA offers sophisticated features like auditing, custom
validations, and powerful relationship mappings to streamline development.

Auditing Support By Spring Data JPA


●​ Spring Data provides sophisticated support to transparently keep track of who created or
changed an entity and when the change happened. To benefit from that functionality, you
have to equip your entity classes with auditing metadata that can be defined either using
annotations or by implementing an interface.
●​ Additionally, auditing has to be enabled either through Annotation configuration or XML
configuration to register the required infrastructure components.
●​ Below are the steps that need to be followed.

Step 1: We need to use the annotations to indicate the audit-related columns inside DB
tables. Spring Data JPA ships with an entity listener that can be used to trigger the capturing
of auditing information. We must register the

You typically define these fields in a base class that other entities can extend.

Generated java

@Data
@MappedSuperclass // Indicates this is not an entity itself, but its fields should be mapped
@EntityListeners(AuditingEntityListener.class) // This listener populates the audit fields
public class BaseEntity {

@CreatedDate // Marks the field to be populated with the creation timestamp


@Column(updatable = false)
private LocalDateTime createdAt;

@CreatedBy // Marks the field to be populated with the principal that created the entity
@Column(updatable = false)
private String createdBy;

@LastModifiedDate // Marks the field to be populated with the last modification timestamp
@Column(insertable = false)
private LocalDateTime updatedAt;

@LastModifiedBy // Marks the field to be populated with the principal that last modified the entity
@Column(insertable = false)
private String updatedBy;
}

Java

●​ @CreatedDate, @CreatedBy, @LastModifiedDate, @LastModifiedBy are the key


annotations that support JPA auditing.
Step 2: Date-related info will be fetched from the server by JPA, but for

This component's job is to tell Spring Security who the current user is.

Generated java

@Component("auditAwareImpl")
public class AuditAwareImpl implements AuditorAware<String> {

@Override
public Optional<String> getCurrentAuditor() {
// Fetches the current user's name from the Spring Security context
return Optional.ofNullable(SecurityContextHolder.getContext().getAuthentication().getName());
}
}

Java

Step 3: Enable JPA auditing by annotating a configuration class with the

You need to tell Spring to use the AuditorAware implementation you just created.

Generated java

@SpringBootApplication
@EnableJpaAuditing(auditorAwareRef = "auditAwareImpl") // Enable auditing and specify the auditor
public class EazyschoolApplication {
// ...
}

Java

Now, whenever you save or update an entity that extends BaseEntity, these four fields
(createdAt, createdBy, updatedAt, updatedBy) will be automatically populated.

Quick Tip: Printing SQL Queries


Do you know we can print the queries that are being formed and executed by Spring Data JPA by
enabling the below properties?

Generated properties

# Shows the formatted SQL in the logs


spring.jpa.show-sql=true

# Formats the SQL to be more readable


spring.jpa.properties.hibernate.format_sql=true

Properties

●​ show-sql property will print the query on the console/logs, whereas the format_sql
property will print the queries in a readable, friendly style.
●​ But please make sure to leverage them in non-prod environments only, as they impact
the performance of the web application.

Spring MVC Custom Validations


We have seen before using Bean validations like @Max, @Min, @Size, etc. We can do validations on
the input received. Now let's try to define custom validations that are specific to our business
requirements. For the same, we need to follow the below steps.

Step 1: Suppose if we have a requirement to not allow some weak passwords inside our user
registration form, we first need to create a custom annotation like below. Here we need to
provide the class name where the actual validation logic is present.

This annotation will be used to mark the field that needs custom validation.

Generated java

@Documented
@Constraint(validatedBy = PasswordStrengthValidator.class) // Link to the validator class
@Target( { ElementType.METHOD, ElementType.FIELD })
@Retention(RetentionPolicy.RUNTIME)
public @interface PasswordValidator {
String message() default "Please choose a strong password";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}

Java

Step 2: We need to create a class that implements the

This class contains the actual business logic for the validation.

Generated java

public class PasswordStrengthValidator implements


ConstraintValidator<PasswordValidator, String> {

List<String> weakPasswords;

@Override
public void initialize(PasswordValidator passwordValidator) {
weakPasswords = Arrays.asList("12345", "password", "qwerty");
}

@Override
public boolean isValid(String passwordField,
ConstraintValidatorContext cxt) {
return passwordField != null && (!weakPasswords.contains(passwordField));
}
}

Java

Step 3: Finally, we can mention the annotation that we created on top of the field inside a
POJO class.

Now you can use your custom annotation just like any other validation annotation.

Generated java
public class Person {
// ... other fields

@NotBlank(message="Password must not be blank")


@Size(min=5, message="Password must be at least 5 characters long")
@PasswordValidator // Our custom validation annotation
private String pwd;
}

Java

Chapter 19: Modeling Relationships with JPA


Databases are all about relationships between data. JPA provides a powerful and declarative way to
model these relationships directly in your entity classes using annotations.

One to One Relationship inside JPA


●​ First, what is a one-to-one relationship? It's a relationship where a record in one entity (table)
is associated with exactly one record in another entity (table).
●​ Below are a few real-life examples of one-to-one relationships:
○​ USER <-> EMAIL (A user has one primary email)
○​ USER <-> ADDRESS (A user has one primary address)
○​ SPOUSE <-> SPOUSE
○​ COUNTRY <-> CAPITAL
●​

Implementing One-to-One in JPA:

●​ Spring Data JPA allows developers to build one-to-one relationships between entities with
simple configurations. For example, if we want to build a one-to-one between Person and
Address entities, then we can configure it like mentioned below.

Generated java

@Data
@Entity
public class Person {
// ... other fields

@OneToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL, targetEntity = Address.class)


@JoinColumn(name = "address_id", referencedColumnName = "addressId", nullable = true)
private Address address;
}

Java

●​ In Spring Data JPA, a one-to-one relationship between two entities is declared by using the
@OneToOne annotation. Using it, we can configure fetchType, cascade effects, and
targetEntity.
●​ The @JoinColumn annotation is used to specify the foreign key column relationship details
between the 2 entities.
○​ name defines the name of the foreign key column in the current (Person) table.
○​ referencedColumnName indicates the field name inside the target entity class
(Address) that the foreign key points to.
○​ nullable defines whether the foreign key column can be null or not.
●​
JPA Fetch Types
●​ Based on the fetch configurations that a developer has done, JPA allows entities to load the
objects with which they have a relationship.
●​ We can declare the fetch value in the @OneToOne, @OneToMany, @ManyToOne, and
@ManyToMany annotations. These annotations have an attribute called fetch that serves to
indicate the type of fetch we want to perform. It has two valid values: FetchType.EAGER and
FetchType.LAZY.
●​ Loading: With LAZY configuration, we are telling JPA that we want to lazily load the relation
entities. So, when retrieving an entity, its relations will not be loaded until we try to refer to
the related entity using a getter method.
●​ Loading: On the contrary, with EAGER it will load the entity's relation entities as well.
●​ Default Behavior: By default, all ...ToMany relationships are LAZY, while all ...ToOne
relationships are EAGER.

Cascade Types
●​ Introduction to Cascade: JPA allows us to propagate entity state changes from Parent
entities to Child entities. This concept is called Cascading in JPA. The cascade configuration
option accepts an array of CascadeTypes.
●​ Cascade Types: The cascade types supported by JPA are as below:
○​ CascadeType.PERSIST: save() or persist() operations cascade to related
entities.
○​ CascadeType.MERGE: merge() operations are merged when the owning entity is
merged.
○​ CascadeType.REFRESH: The child entity also gets reloaded from the database
whenever the parent entity is refreshed.
○​ CascadeType.REMOVE: Propagates the remove operation from parent to child entity.
○​ CascadeType.DETACH: Detaches all child entities if a "manual detach" occurs for the
parent.
○​ CascadeType.ALL: Is shorthand for all of the above cascade operations.

●​ Best Practices:
○​ Cascading makes sense only for Parent -> Child associations (where the Parent
entity state transition is cascaded to its Child entities). Cascading from Child to
Parent is not very useful and is not recommended.
○​ There is no default cascade type in JPA. By default, no operation is cascaded.

Chapter 20: Advanced Spring Security Implementation


Previously, we used in-memory authentication, which is not suitable for production. The proper way
is to validate user credentials against a persistent store like a database. This requires creating a
custom authentication provider.

Spring Security AuthenticationProvider


●​ As of now, we are performing the login operation using InMemoryAuthentication. But the
ideal way is to perform a login check against a DB table or any other storage system,
which is more secure.
●​ For the same, Spring Security allows us to write our own custom logic to authenticate a
user based on our requirements by implementing the AuthenticationProvider interface.
Below is the sample implementation of the same.

Example Custom Authentication Provider:

Generated java

@Component

public class EazySchoolUsernamePwdAuthenticationProvider implements AuthenticationProvider


{
@Autowired

private PersonRepository personRepository; // Assuming a repository to fetch user data

@Override

public Authentication authenticate(Authentication authentication) throws


AuthenticationException {

String name = authentication.getName();

String pwd = authentication.getCredentials().toString();

// Your custom logic to authenticate the user

// For example, fetching the user from the database

Person person = personRepository.readByEmail(name);

if (person != null && person.getPersonId() > 0 &&

pwd.equals(person.getPwd())) { // Insecure password check! We'll fix this.

return new UsernamePasswordAuthenticationToken(

name, null, new ArrayList<>());

} else {

throw new BadCredentialsException("Invalid credentials!");

@Override

public boolean supports(Class<?> authentication) {

// This provider supports the standard username/password authentication token


return authentication.equals(UsernamePasswordAuthenticationToken.class);

Java

This provider is registered as a component, so Spring will automatically pick it up and use it in the
authentication process.

How Passwords are Validated W/O PasswordEncoder


The diagram illustrates a highly insecure but simple authentication flow.

1.​ User entered credentials: The user provides a username ("Admin") and a password
("12345") in the login form.
2.​ Match?: The backend receives these credentials.
3.​ Load plain credentials stored in DB: It fetches the user's record from the database, which
contains the password stored in plain text.
4.​ The backend directly compares the entered password with the stored password.
○​ If they match -> Login Success.
○​ If they don't match -> Login Fails.
5.​

The Problem:​
Storing credentials in plain text inside a storage system like a Database has Integrity and
Confidentiality issues. So this is not recommended for PROD applications. If the database is
ever compromised, all user passwords will be exposed.

Passwords Management: Different Ways of Pwd Management


There are three common techniques for handling sensitive data like passwords. Only one is suitable
for security.

●​ Encoding:
○​ Encoding is defined as the process of converting data from one form to another
and has nothing to do with cryptography.
○​ It involves no secret and is completely reversible. Anyone who knows the algorithm
can reverse it.
○​ Encoding can't be used for securing data. Below are the various publicly available
algorithms used for encoding.
○​ Ex: ASCII, BASE64, UNICODE
●​ Encryption:
○​ Encryption is defined as the process of transforming data in such a way that
guarantees confidentiality.
○​ To achieve confidentiality, encryption requires the use of a secret which, in
cryptographic terms, we call a "key".
○​ Encryption can be reversible by using decryption with the help of the "key". As
long as the "key" is confidential, encryption can be considered secured.
●​ Hashing:
○​ In hashing, data is converted to a hash value using some hashing function.
○​ Data once hashed is non-reversible. One cannot determine the original data from a
hash value generated.
○​ Given some arbitrary data along with the output of a hashing algorithm, one can
verify whether this data matches the original input data without needing to see the
original data. This is perfect for password validation.
●​

Quick Tip: PasswordEncoder in Spring Security


Do you know Spring Security provides various s to help developers with hashing of the secured data
like passwords?

To use one, you simply declare it as a bean in your security configuration.

Different Implementations of

●​ NoOpPasswordEncoder (No hashing, stores in plain text) - DEPRECATED and INSECURE.


●​ StandardPasswordEncoder - Deprecated.
●​ Pbkdf2PasswordEncoder - A strong, standard hashing algorithm.
●​ BCryptPasswordEncoder - (Most Commonly Used) A very strong algorithm that
incorporates a "salt" to protect against rainbow table attacks.
●​ SCryptPasswordEncoder - Another strong algorithm, designed to be even more
computationally intensive than bcrypt.

How Passwords are Validated With Hashing & PasswordEncoder


This is the secure, recommended approach.

1.​ User entered credentials: The user provides their password ("12345").
2.​ Hashing Algorithm: The backend takes the entered password and runs it through the same
hashing algorithm (e.g., BCrypt) that was used when the user registered. This produces a
hash: f32c........adv.
3.​ Retrieve already stored password hash from DB: The backend retrieves the user's stored
password hash from the database: g22h........bef. Note that this is also a hash, not
plain text.
4.​ Hash value matches?: The PasswordEncoder's matches() method is used to compare
the newly generated hash with the stored hash. This method is smart enough to handle the
salt.
○​ If they match -> Login Success.
○​ If they don't match -> Login Fails.
5.​

The Advantage:​
Managing passwords with Hashing is the recommended approach for PROD web applications.
With PasswordEncoders like BCryptPasswordEncoder, Spring Security makes our life easy. At no
point is the plain-text password stored or compared directly.

Quick Tip: Disabling JPA Validation


Do you know we can disable the javax.validation (now jakarta.validation) validations by
Spring Data JPA using the below property?

This can be useful during development or in specific scenarios where you want to bypass the
validation that would normally be triggered before a JPA operation (like save() or update()).

Generated properties

spring.jpa.properties.javax.persistence.validation.mode=none
Properties

Chapter 21: Advanced Relationship Mappings


One to Many & Many to One Relationship inside JPA
●​ A one-to-many relationship refers to the relationship between two entities/tables A and B in
which one element/row of A may be linked to many elements/rows of B, but a member of B
is linked to only one element/row of A.
●​ The opposite of a one-to-many relationship is a many-to-one relationship.
●​ Below are a few real-life examples of one-to-many and many-to-one relationships:
○​ CLASS <-> STUDENTS (One Class has Many Students; Many Students belong to One
Class)
○​ BOOK <-> PAGES (One Book has Many Pages; Many Pages belong to One Book)
●​

Implementing the Relationships in JPA:

●​ Spring Data JPA allows developers to build one-to-many & many-to-one relationships
between entities with simple configurations.
●​ Below are the sample configurations between Class and Person (representing a student).

Person Entity (

Generated java

@Entity

public class Person extends BaseEntity {

// ... other fields

@ManyToOne(fetch = FetchType.LAZY, optional = true)

@JoinColumn(name = "class_id", referencedColumnName = "classId", nullable = true)

private EazyClass eazyClass;

Java

●​ The @ManyToOne annotation is used to define a many-to-one relationship between two


entities. The child entity, that has the join column, is called the owner of the relationship.
●​ The @JoinColumn annotation is used to specify the foreign key column details.

EazyClass Entity (

Generated java
@Entity

@Table(name = "class")

public class EazyClass extends BaseEntity {

// ... other fields

@OneToMany(mappedBy = "eazyClass", fetch = FetchType.LAZY,

cascade = CascadeType.PERSIST, targetEntity = Person.class)

private Set<Person> persons;

Java

●​ A one-to-many relationship between two entities is defined by using the @OneToMany


annotation.
●​ It also declares the mappedBy element to indicate the entity that owns the bidirectional
relationship. Usually, the child entity is the one that owns the relationship, and the parent
entity contains the @OneToMany annotation. mappedBy should be set to the name of the field
on the child side that refers back to this parent.

Many to Many inside JPA


●​ A many-to-many relationship refers to the relationship between two entities/tables A and B
in which one element/row of A is associated with many elements/rows of B and vice versa.
●​ Below are a few real-life examples of many-to-many relationships:
○​ COURSES <-> STUDENTS (Many students can enroll in many courses)
○​ ORDERS <-> PRODUCTS (Many orders can contain many products)
●​

Implementing Many-to-Many in JPA:

●​ Spring Data JPA allows developers to build many-to-many relationships between entities
with simple configurations. Below are the sample configurations between Courses and
Person.

Person Entity (One side of the relationship):

Generated java

@Entity

public class Person extends BaseEntity {

// ...

@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.PERSIST)

@JoinTable(name = "person_courses",

joinColumns = {

@JoinColumn(name = "person_id", referencedColumnName = "personId")

},

inverseJoinColumns = {

@JoinColumn(name = "course_id", referencedColumnName = "courseId")

})

private Set<Courses> courses = new HashSet<>();

Java
Courses Entity (The other side):

Generated java

@Entity

public class Courses extends BaseEntity {

// ...

@ManyToMany(mappedBy = "courses", fetch = FetchType.EAGER, cascade =


CascadeType.PERSIST)

private Set<Person> persons = new HashSet<>();

Java

●​ A many-to-many relationship between two entities is defined by using the @ManyToMany


annotation.
●​ The @JoinTable annotation defines the join/link table that sits between the two entities.
●​ In a bidirectional relationship of @ManyToMany, only one entity can own the relationship.
Here we choose Person as the owning entity. We usually mention the mappedBy parameter
on the non-owning entity.

Why do we really need a third table in a Many-to-Many relationship?

Let's assume we have the tables and records below which have a many-many relationship between
them.

●​ COURSES Table: (COURSE ID, NAME, FEES)


●​ PERSONS Table: (PERSON ID, NAME, EMAIL)
If we need to represent multiple courses that a same student enrolled, the best way is to maintain a
middle third table like shown below. Otherwise, we would need to maintain a lot of duplicate rows
inside the Person table or have a complex, non-normalized column structure.

The PERSON_COURSES Table simply links the two, containing PERSON_ID and COURSE_ID,
resolving the many-to-many relationship into two one-to-many relationships.

Chapter 22: Sorting and Pagination with Spring Data JPA


When dealing with large datasets, it's inefficient and impractical to fetch all records at once. We need
mechanisms to retrieve data in manageable chunks (pagination) and in a meaningful order (sorting).
Spring Data JPA provides excellent, easy-to-use support for both, moving beyond simple CRUD
operations to handle real-world data retrieval scenarios gracefully.

Sorting with Spring Data JPA


Introduction to Sorting

●​ Spring Data JPA supports Sorting of query results with easier configurations, allowing you to
order the data returned from your repositories.
●​ Spring Data JPA provides default implementations of Sorting with the help of the
PagingAndSortingRepository interface, which extends CrudRepository with sorting
capabilities.
●​ There are two primary ways to achieve Sorting in Spring Data JPA:
1.​ Static Sorting: The sort order is fixed in the code.
2.​ Dynamic Sorting: The sort order is determined at runtime, often based on user
input.
●​

Static Sorting

●​ Static sorting refers to the mechanism where the retrieved data is always sorted by
specified columns and directions.
●​ The columns and sort directions are defined at the development time within the repository
method definition and cannot be changed at runtime.
●​ The most common way to implement static sorting is by using the OrderBy keyword in a
derived query method name.

Example of Static Sorting:

Generated java

public interface PersonRepository extends JpaRepository<Person, Long> {

// This query will always find people by name and sort the results
// by their age in descending order.
List<Person> findByNameOrderByAgeDesc(String name);
}
When this method is called, the underlying query generated by Spring Data will always include an
ORDER BY age DESC clause.

Dynamic Sorting

●​ By using dynamic sorting, you can choose the sorting column and direction at runtime to
sort the query results.
●​ Dynamic sorting provides more flexibility by allowing you to pass a Sort object as a
parameter to your query method.
●​ The Sort class is a simple specification object that provides sorting options for database
queries. It allows you to build complex sorting criteria programmatically.

Example of Dynamic Sorting:

Generated java

// In your service layer or controller


public List<Person> findSortedPeople(String direction, String... properties) {
Sort.Direction sortDirection = Sort.Direction.fromString(direction);
Sort sort = Sort.by(sortDirection, properties);
return personRepository.findAll(sort);
}

// Example usage:
// To sort by name ascending: findSortedPeople("ASC", "name")
// To sort by age descending, then by name ascending:
Sort sortByAgeDesc = Sort.by("age").descending();
Sort sortByNameAsc = Sort.by("name").ascending();
Sort complexSort = sortByAgeDesc.and(sortByNameAsc);
List<Person> people = personRepository.findAll(complexSort);

Pagination with Spring Data JPA


Introduction to Pagination

●​ Spring Data JPA supports Pagination, which helps to easily manage and display large
amounts of data in various pages inside web applications. This prevents loading thousands
of records into memory at once.
●​ Just like the special Sort parameter for dynamic sorting, Spring Data JPA supports another
special parameter called Pageable for paginating the query results.
●​ We can combine both pagination and dynamic sorting within a single Pageable object.

Dynamic Pagination

●​ Whenever we want to apply pagination to query results, all we need to do is just add a to
the query method definition and set the return type to Page<T>.
●​ The Pageable object is typically created using PageRequest.of(...).
●​ The returned Page object is a powerful construct. It not only contains the list of records for
the requested page (getContent()) but also provides rich metadata about the entire
dataset, such as:
○​ Total number of records (getTotalElements())
○​ Total number of pages (getTotalPages())
○​ The current page number (getNumber())
○​ The number of elements on the current page (getNumberOfElements())
○​ Whether it's the first or last page (isFirst(), isLast())
●​

Example of

Generated java

public interface ContactRepository extends PagingAndSortingRepository<Contact, Integer> {


// A method that finds contacts by status and supports pagination
Page<Contact> findByStatus(String status, Pageable pageable);
}

// In your service layer


public Page<Contact> findOpenMessages(int pageNumber, int pageSize) {
// Create a Pageable request for a specific page and size
Pageable pageable = PageRequest.of(pageNumber, pageSize, Sort.by("createdAt").descending());
return contactRepository.findByStatus("OPEN", pageable);
}
Spring Data JPA Custom Queries
While derived queries are powerful, they have their limits. For more complex logic, you'll need to
write your own queries.

Introduction

●​ Derived queries are good as long as they are not complex. As the number of query
parameters goes beyond 3 to 4, the method names become long and unreadable, and you
need a more flexible strategy.
●​ For such complicated or custom scenarios, Spring Data JPA allows developers to write their
own queries with the help of the below annotations:
○​ @Query: For defining queries directly on a repository method.
○​ @NamedQuery: For defining a JPQL query centrally on an entity.
○​ @NamedNativeQuery: For defining a native SQL query centrally on an entity.
●​

@Query Annotation

●​ The @Query annotation defines queries directly on repository methods. This gives you full
flexibility to run any query without following the method naming conventions.
●​ With the help of the @Query annotation, we can write queries in the form of JPQL or Native
SQL.
●​ When we are writing a native SQL query, then we need to mention the nativeQuery =
true attribute inside the @Query annotation.

Named Queries

●​ For bigger applications where they may have thousands of queries scattered across the
application, it would make sense for them to maintain all these queries in a single place
logically. This is the purpose of Named Queries. They are defined on the entity and
referenced by name in the repository.
●​ We can create named queries easily with the below annotations on top of an entity class:
○​ @NamedQuery: Used to define a JPQL named query.
○​ @NamedNativeQuery: Used to define a native SQL named query.
●​

JPQL inside Spring Data JPA


Introduction

●​ The Java Persistence Query Language (JPQL) is a platform-independent, object-oriented


query language defined as part of the Java Persistence API (JPA) specification.
●​ JPQL is used to make queries against entities stored in a relational database. It is heavily
inspired by SQL, and its queries resemble SQL queries in syntax, but operate against JPA
entity objects and their fields rather than directly with database tables and columns.
●​ The only drawback of using JPQL is that it supports a subset of the SQL standard, so it
may not be a great choice for highly complex, database-specific queries.

JPQL Example

Below is an example of a JPQL query. You can observe we are using the entity names ( present
inside it instead of using table and column names.

Generated java

public interface ContactRepository extends CrudRepository<Contact, Long> {

@Query("SELECT c FROM Contact c WHERE c.contactId = ?1 ORDER BY c.createdAt DESC")


List<Contact> findByIdOrderByCreatedAtDesc(long id);
}

Sorting Examples
Here are various ways to implement sorting with custom queries.

1. Static Sorting using derived query from method name:

Generated java
// Method name defines the sorting order
List<Person> findByNameOrderByAgeDesc(String name);

2. Static Sorting using

Generated java

// JPQL query defines the sorting order


@Query("SELECT p FROM person p WHERE p.age > ?1 ORDER BY p.name DESC")
List<Person> findByAgeGreaterThanJPQL(int age);

3. Static Sorting using

Generated java

// Native SQL query defines the sorting order


@Query(value = "SELECT * FROM person p WHERE p.name = :givenName ORDER BY p.age ASC",
nativeQuery = true)
List<Person> findByGivenNameNativeSQL(@Param("givenName") String givenName);

4. Static Sorting using

These annotations are placed on the entity class.

Generated java
@NamedQuery(name = "Person.findByAgeGreaterThanNamedJPQL",
query = "SELECT p FROM Person p WHERE p.age > ?1 ORDER BY p.name ASC")
@NamedNativeQuery(name = "Person.findAllNamedNativeSQL",
query = "SELECT * FROM person p ORDER BY p.age DESC",
resultClass = Person.class)
@Entity
public class Person extends BaseEntity {
// ...
}

5. Dynamic Sorting using ​


The Sort parameter can be passed with @Query and @NamedQuery annotations for JPQL queries.

Generated java

@Query("SELECT p FROM Person p WHERE p.name = :name")


List<Person> findByName(@Param("name") String name, Sort sort);

// Calling the method


Sort sort = Sort.by("name").descending().and(Sort.by("age"));
List<Person> persons = personRepository.findByName("John", sort);

Important Note: Spring Data JPA does not support dynamic sorting for native SQL queries.
The Sort parameter will be ignored because Spring cannot safely modify a native SQL string to add
an ORDER BY clause. If you need dynamic sorting with native SQL, you must build the query string
yourself, for example, by using the Spring JdbcTemplate.

Pagination Examples
1. Below is an example where we are telling JPA to fetch the first page by considering the
total page size as 5.

Generated java

// Page number is 0-indexed. This gets the first page with 5 items.
Pageable pageable = PageRequest.of(0, 5);
Page<Contact> msgPage = contactRepository.findByStatus("OPEN", pageable);

2. Below is an example where we are applying both pagination & sorting dynamically based
on the input received.

Generated java

public Page<Contact> findMsgsWithOpenStatus(int pageNum, String sortField, String sortDir) {


int pageSize = 5;
// Build sort direction dynamically
Sort sort = sortDir.equalsIgnoreCase("asc") ?
Sort.by(sortField).ascending() : Sort.by(sortField).descending();

// Create Pageable request (page number is 0-indexed)


Pageable pageable = PageRequest.of(pageNum - 1, pageSize, sort);

Page<Contact> msgPage = contactRepository.findByStatus("OPEN", pageable);


return msgPage;
}

Important Note: Dynamic sorting is not supported by named queries. So while using Pagination
along with Named Queries, make sure that the Pageable instance does not contain a Sort object,
as it will be ignored and may cause confusion.
@Query Examples
1. ​
Parameters are referenced by their position, starting from 1 (?1, ?2, etc.).

Generated java

// Positional parameters
@Query("SELECT c FROM Contact c WHERE c.status = ?1 AND c.name = ?2 ORDER BY c.createdAt
DESC")
List<Contact> findByGivenQueryOrderByCreatedAtDesc(String status, String name);

2. ​
Parameters are referenced by name (:status, :name). This is generally more readable and less
error-prone than positional parameters.

Generated java

// named parameters
@Query("SELECT c FROM Contact c WHERE c.status = :status AND c.name = :name ORDER BY
c.createdAt DESC")
List<Contact> findByGivenQueryOrderByCreatedAtDesc(@Param("status") String status,
@Param("name") String name);

3. Using

Generated java

@Transactional
@Modifying
@Query("UPDATE Contact c SET c.status = ?1 WHERE c.contactId = ?2")
int updateStatusById(String status, int id);

●​ Whenever we are using queries that change the state of the database, they should be
treated differently. We need to explicitly tell Spring Data JPA that our custom query changes
the data by annotating the repository method with an additional @Modifying annotation. It
will then execute the custom query as an update operation.
●​ On whichever method/class we declare @Transactional, the boundary of the transaction
starts, and the boundary ends when the method execution completes. For example, if you
are updating entity1 and entity2, and an exception occurs while saving entity2, then because
entity1 is in the same transaction, its change will be rolled back along with entity2. Any
exception will result in a rollback of all JPA operations within that transaction boundary.

@NamedQuery & @NamedNativeQuery Examples


1.

Generated java

@Entity
@NamedQuery(name="Contact.findOpenMsgs",
query="SELECT c FROM Contact c WHERE c.status = :status")
public class Contact extends BaseEntity {
// ...
}

2. ​
For native queries, you must also specify the entity class (resultClass) that the result should be
mapped to.
Generated java

@Entity
@NamedNativeQuery(name = "Contact.findOpenMsgsNative",
query = "SELECT * FROM contact_msg c WHERE c.status = :status",
resultClass = Contact.class)
public class Contact extends BaseEntity {
// ...
}

Usage Note: For @NamedQuery, as long as the method name inside the Repository class matches
with the name of the query (e.g., findOpenMsgs), it should be good. Where as for
@NamedNativeQuery, apart from the query name & method name match, we should also mention
@Query(nativeQuery = true) on top of the Repository method to hint to Spring Data that it's a
native query.

Quick Tip: Multiple Named Queries


Do you know we can create multiple named queries and named native queries using the container
annotations @NamedQueries and @NamedNativeQueries? Below is the syntax of the same.

Generated java

@Entity
@NamedQueries({
@NamedQuery(name = "Contact.one", query = "SELECT c FROM Contact c ..."),
@NamedQuery(name = "Contact.two", query = "SELECT c FROM Contact c ...")
})
@NamedNativeQueries({
@NamedNativeQuery(name = "Contact.nativeOne", query = "SELECT * ...", resultClass =
Contact.class),
@NamedNativeQuery(name = "Contact.nativeTwo", query = "SELECT * ...", resultClass =
Contact.class)
})
public class Contact extends BaseEntity {
// ...
}

Chapter 23: Building and Consuming REST Services


So far, we have focused on building traditional web applications where the server renders the full
HTML view. Now, we'll shift to building backend services that communicate with clients (like mobile
apps or single-page web apps) by exchanging data, typically in JSON format. This is achieved using
REST services.

Implementing REST Services


●​ REST (Representational State Transfer) services are one of the most often encountered
ways to implement communication between two web apps. REST offers access to
functionality the server exposes through endpoints a client can call.
●​ Below are the different use cases where REST services are being used most frequently
these days:
○​ Mobile App <-> Backend Server: A native mobile app communicates with a
backend to fetch and update data.
○​ Web App (Angular, React) <-> Backend Server: A modern, client-side web
application communicates with a backend.
○​ Backend Server 1 <-> Backend Server 2: Microservices communicate with each
other.
●​

Implementing REST Services with Spring MVC


Implementing a REST service in Spring is very similar to building a regular web controller, with one
key addition: the @ResponseBody annotation.

Below is the sample code implementing a REST Service using Spring MVC style but only with the
addition of the @ResponseBody annotation.

Generated java

@Controller

public class ContactRestController {


@Autowired

ContactRepository contactRepository;

@GetMapping("/getMessagesByStatus")

@ResponseBody // This is the key annotation

public List<Contact> getMessagesByStatus(@RequestParam(name = "status") String status) {

return contactRepository.findByStatus(status);

Java

The ​
The @ResponseBody annotation tells the DispatcherServlet that the controller's action will not
return a view name, but the data sent directly in the HTTP response body. Spring automatically
uses message converters (like Jackson for JSON) to serialize the return object (List<Contact>)
into the appropriate format based on the request's Accept header.

Spring MVC Architecture with REST Service


When implementing REST endpoints, the Spring MVC flow changes slightly.

●​ The ViewResolver is no longer needed.


●​ The app no longer needs a view resolver because the client needs the data returned by the
controller's action directly.
●​ Once the controller's action completes, the dispatcher servlet returns the HTTP response
without rendering any view.

The Modified Flow:

1.​ Web Client makes a request.


2.​ Tomcat passes the request to the DispatcherServlet.
3.​ DispatcherServlet consults HandlerMapping.
4.​ DispatcherServlet invokes the Controller. The controller method (annotated with
@ResponseBody) returns data (e.g., a List<Contact>).
5.​ DispatcherServlet uses message converters to write the data directly to the HTTP
response.
6.​ Tomcat sends the HTTP response (with the JSON body) back to the client.

The ViewResolver step is completely skipped.

The @RestController Annotation


Instead of mentioning @ResponseBody on each method inside a Controller class, which is duplicate
code, Spring offers the @RestController annotation, a combination of @Controller and
@ResponseBody.

@RestController = @Controller + @ResponseBody

This is a convenience annotation that marks the class as a controller where every method inherits
the @ResponseBody behavior.

Example with

Generated java

@RestController // No need for @ResponseBody on each method


public class ContactRestController {

@Autowired

ContactRepository contactRepository;

@GetMapping("/getMessagesByStatus")

public List<Contact> getMessagesByStatus(@RequestParam(name = "status") String status) {

return contactRepository.findByStatus(status);

Java

Different Annotations & Classes in REST

Annotation/Class Description

@RestController Can be used to put on top of a call. This will save


developers from mentioning @ResponseBody on each
method.

@ResponseBody Can be used on top of a method to build a REST API


when we are using @Controller on top of a Java class.

ResponseEntity<T> Allows developers to send the response body, status,


and headers on the HTTP response. It provides full
control over the response.
@RestControllerAdvice Is used to mark the class as a REST controller advice.
Along with @ExceptionHandler, this can be used to
handle exceptions globally inside the REST API part of
the app.

HttpEntity<T> / Allows developers to receive the request body and


RequestEntity<T> headers in an HTTP request.

& Is used to receive the request body and header


individually.

Cross-Origin Resource Sharing (CORS)


●​ CORS is a protocol that enables scripts running on a browser client to interact with resources
from a different origin. For example, if a UI app wishes to make an API call running on a
different domain, it would be blocked from doing so by default due to CORS. So CORS is
not a security issue/attack, but the default protection provided by browsers to stop
sharing the data/communication between different origins.
●​ "other origins" means the URL being accessed differs from the location that the JavaScript
is running from, by having:
○​ a different scheme (HTTP or HTTPS)
○​ a different domain
○​ a different port
●​

By default, the browser will block this communication due to the Same-Origin Policy.​
UI App running on ---X---> Backend API running on

Enabling CORS
●​ If we have a valid scenario, where a Web App UI deployed on a server is trying to
communicate with a REST service deployed on another server, then these kinds of
communications we can allow with the help of the @CrossOrigin annotation. @CrossOrigin
allows clients from any domain to consume the API.
●​ @CrossOrigin annotation can be mentioned on top of a class or method like mentioned
below.
○​ @CrossOrigin(origins = "http://localhost:8080") // Will allow on specified
domain
○​ @CrossOrigin(origins = "*") // Will allow any domain
●​

After CORS is enabled on the backend, communication is allowed.​


UI App running on ---✓---> Backend API running on

Quick Tip: Controlling JSON Output with Jackson


Do you know the Jackson library provides annotations to control the way we send data in a REST
API JSON response? Below are a few annotations.

●​ @JsonProperty: This annotation can be mentioned on top of any field of a POJO class.
While sending the JSON response, instead of sending the field name, Jackson will send the
property name that we mentioned.
●​ Generated java

@JsonProperty("person_name")

private String name;

Java

●​ The JSON output will be {"person_name": "John"} instead of {"name": "John"}.


●​ @JsonIgnore: This annotation will make sure to not send the information present inside the
given field. This is useful to filter the data which is sensitive or unnecessary in the response.
●​ Generated java

@JsonIgnore

private LocalDateTime createdAt;

Java
●​ We can also use @JsonIgnoreProperties(value = { "createdAt" }) on top of a
POJO class if we want to mention multiple fields.

Consuming REST Services


●​ Apart from building the Rest service, often we may need to consume the Rest Services
exposed by other third-party vendors. So knowing how to consume Rest services is equally
important.
●​ Below are the most commonly used approaches that are provided by the Spring framework.
●​ OpenFeign: A tool offered by the Spring Cloud project. Using this, very similar to how we
build Repositories with Spring Data JPA, we just need to write interfaces but not
implementation code. It's a declarative REST client.
●​ RestTemplate: A well-known tool developers have been using since Spring 3 to call REST
endpoints. RestTemplate is often used today in Spring apps, but this is deprecated in
favor of .
●​ WebClient: Created as part of the Spring Web Reactive module and will be replacing the
classic RestTemplate. This is introduced to support all modes of invocation like Sync and
Async (non-blocking). This is the modern, recommended approach.

Consuming REST Services using OpenFeign


In order to consume the REST services using OpenFeign, we need to follow the below steps.

Step 1: After adding all the required dependencies inside the

You'll need spring-cloud-starter-openfeign. You also need to add @EnableFeignClients to


your main application class.

Generated java

@FeignClient(name = "contact", url = "http://localhost:8080/api/contact",

configuration = ProjectConfiguration.class)

public interface ContactProxy {

@RequestMapping(method = RequestMethod.GET, value = "/getMessagesByStatus")

@Headers(value = "Content-Type: application/json")


public List<Contact> getMessagesByStatus(@RequestParam("status") String status);

Java

Step 2: If we need to send the authentication details, then create a bean with the required
details.

Generated java

@Bean

public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {

return new BasicAuthRequestInterceptor("[email protected]", "admin");

Java

This interceptor will automatically add the Authorization header to every request made by this
Feign client.

Step 3: Finally, we are good to use the proxy object to make a Rest call like shown below.

Generated java

@Autowired

ContactProxy contactProxy;

@GetMapping("/getMessages")

public List<Contact> getMessages(@RequestParam("status") String status) {

return contactProxy.getMessagesByStatus(status);

Java

Consuming REST Services using RestTemplate


In order to consume the REST services using RestTemplate, we need to follow the below steps.
Step 1: After adding all the required dependencies inside the

Generated java

@Bean

public RestTemplate restTemplate() {

RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();

return restTemplateBuilder.basicAuthentication("[email protected]", "admin").build();

Java

Step 2: Using

Generated java

@Autowired

RestTemplate restTemplate;

@PostMapping("/saveMsg")

public ResponseEntity<Response> saveMsg(@RequestBody Contact contact) {

String uri = "http://localhost:8080/api/contact/saveMsg";

HttpHeaders headers = new HttpHeaders();

headers.add("invocationFrom", "RestTemplate");

HttpEntity<Contact> httpEntity = new HttpEntity<>(contact, headers);

ResponseEntity<Response> responseEntity = restTemplate.exchange(

uri, HttpMethod.POST, httpEntity, Response.class);


return responseEntity;

Java

Consuming REST Services using WebClient


In order to consume the REST services using WebClient, we need to follow the below steps. You'll
need spring-boot-starter-webflux for this.

Step 1: After adding all the required dependencies inside the

Generated java

@Bean

public WebClient webClient() {

return WebClient.builder()

.filter(ExchangeFilterFunctions

.basicAuthentication("[email protected]", "admin"))

.build();

Java

Step 2: Using

Generated java

@Autowired

WebClient webClient;
@PostMapping("/saveMessage")

public Mono<Response> saveMessage(@RequestBody Contact contact) {

String uri = "http://localhost:8080/api/contact/saveMsg";

return webClient.post().uri(uri)

.header("invocationFrom", "WebClient")

.body(Mono.just(contact), Contact.class)

.retrieve()

.bodyToMono(Response.class);

Java

Chapter 24: Hypermedia and Spring Data REST


We've seen how to manually build REST APIs. But what if the framework could build them for us,
just by looking at our repositories? This is the magic of Spring Data REST.
Spring Data REST
●​ Apart from doing a magic of automatically creating repository implementations based on
interfaces you define in your code, Spring Data has another feature that can help you define
REST APIs for repositories created by Spring Data.
●​ For the same, we have Spring Data REST, which is another member of the Spring Data
family.
●​ To start using Spring Data REST in our project, we just need to add the following
dependency to our pom.xml.

Maven Dependency:

Generated xml

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-rest</artifactId>

</dependency>

Xml

What Spring Data REST Does:

●​ Automatic API Generation: We just need to add the Spring Data REST starter project, and
believe it or not, that's the only change expected from the Developer. Post that, the
application gets auto-configuration that enables automatic creation of a REST API for any
repositories that were created by Spring Data.
●​ High-Quality Endpoints: The REST APIs will be created for any kind of implementations
like Spring Data JPA, Spring Data Mongo, etc. The REST endpoints that Spring Data REST
generates are at least as good as (and possibly even better than) the ones Developers
create 😊.
●​ Checking the APIs: We can check all the REST APIs exposed by Spring Data REST by
opening the URL http://localhost:8080/profile in the browser.
●​ HAL Explorer Integration: By adding the HAL Explorer along with Spring Data REST, we
can look at all the APIs exposed by Spring Data REST by opening the
http://localhost:8080/ URL in the browser, assuming that your App started at the 8080
port itself.

HAL Explorer
●​ HAL (Hypertext Application Language) is a simple format that gives a consistent and easy
way to hyperlink between resources in your API.
●​ Adopting HAL will make your API explorable, and its documentation easily discoverable
from within the API itself. In short, it will make your API easier to work with and therefore
more attractive to client developers.
●​ To start using HAL explorer along with Spring Data REST in our project, we just need to add
the following dependency to our pom.xml.

Maven Dependency:

Generated xml

<dependency>

<groupId>org.springframework.data</groupId>

<artifactId>spring-data-rest-hal-explorer</artifactId>

</dependency>

Xml

Using HAL Explorer:​


We can check the HAL explorer by opening the URL http://localhost:8080/ in the browser. The
UI is an Angular-based web application that lets you easily explore HAL and HAL-FORMS based
HTTP Responses. It provides a browsable, interactive documentation for your API, generated
automatically.

Quick Tip: Customizing Spring Data REST


Do you know we can change the default path exposed by Spring Data REST using the below
configurations? This will change the default path as per your configurations.

●​ Change the base path for all repositories:

Generated properties

spring.data.rest.basePath=/data-api

Properties

●​ Using the @RepositoryRestResource annotation, we can also control the way the paths of
the repositories are being exposed.
●​ Generated java

@RepositoryRestResource(path = "courses")

public interface CoursesRepository extends JpaRepository<Courses, Integer> {

// ...

Java

●​ Now, this repository will be exposed at /data-api/courses instead of the default


/data-api/coursesRepository.
●​ For some reason, if you don't want to expose a Repository, then we can do the below
configuration on top of the Repository class.

Generated java

@RepositoryRestResource(exported = false)

public interface SensitiveDataRepository extends JpaRepository<SensitiveData, Integer> {

// ...
}

Java

Chapter 25: Production-Ready Features: Logging,


Properties, and Profiles
Building an application is one thing; running it in production is another. Spring Boot provides
excellent support for features that are critical for production environments.
Logging inside SpringBoot
Introduction

●​ By default, we don't have to worry about logging if we are using Spring Boot. Most of the
configurations and logging are done by Spring Boot itself.
●​ Usually, we have the following types (levels) of logging:
○​ FATAL (Logback doesn't have a FATAL level; it maps to ERROR)
○​ ERROR
○​ WARN
○​ INFO
○​ DEBUG
○​ TRACE
●​

Logging Types

●​ In Java, we have many logging frameworks like Java Util logging, Log4j2, SLF4J, and
Logback. By default, if you use the "Starters", Logback is used for logging.
●​ Appropriate Logback routing is also included in Spring Boot to ensure that dependent
libraries that use Java Util Logging, Commons Logging, Log4j, or SLF4J all work correctly.
●​ By default, ERROR-level, WARN-level, and INFO-level messages are logged. But we can
change them based on our requirements and environments.

Configuring Logging
●​ We can enable debug or trace logging by mentioning the below properties inside the
application.properties file.

Generated properties

debug=true

trace=true

Properties
●​ Alternatively, we can provide the flags while starting the application like java -jar
myapp.jar --debug.
●​ The way logging works is if we enable trace, then all above severities like Debug, Info, Warn
& Error are also printed. Suppose if we enable error logging only, then all lower severities like
Warn, Info, Debug, Trace will not be logged.
●​ If needed, we can control the logging at the package level by mentioning properties like
below inside application.properties.
●​ Generated properties

# Set the root logger to INFO level

logging.level.root=INFO

# Set the logging level for our aspects package to ERROR

logging.level.com.eazybytes.eazyschool.aspects = ERROR

Properties

●​ It is often useful to be able to group related loggers so that they can all be configured at the
same time. For example, you may have a requirement to change the logging levels for all my
project-related packages very frequently. To help with this, Spring Boot allows you to define
logging groups. Below is the sample configuration.

Generated properties

# Initialize log group

logging.group.eazyschool_error=com.eazybytes.eazyschool.aspects,com.eazybytes.eazyschool.controlle
r
# Set log level to log group

logging.level.eazyschool_error=ERROR

Properties

●​ If your terminal supports ANSI, color output is used to aid readability of your logs. You can
set the below property to enable the same.

Generated properties

spring.output.ansi.enabled=ALWAYS

Properties

Anatomy of a Spring Boot Log Message


Below is the default logging format in Spring Boot. Below is a sample logger message and format
details of it.

2025-01-21 08:02:46.035 INFO 15084 --- [restartedMain]


c.e.e.EazyschoolApplication : Started EazyschoolApplication in 5.189 seconds
(JVM running for 2123.116)

●​ Date and Time: Millisecond precision and easily sortable.


●​ Log Level: ERROR, WARN, INFO, DEBUG, or TRACE.
●​ Process ID.
●​ ---: A separator to distinguish the start of actual log messages.
●​ Thread name: Enclosed in square brackets (may be truncated for console output).
●​ Logger name: This is usually the source class name (often abbreviated).
●​ The log message.

By default, Spring Boot logs only to the console and does not write log files. If you want to write log
files in addition to the console output, you can create a file with the name logback.xml inside the
classpath and define all our logging requirements in it.

Quick Tip: Lombok for Logging


Do you know Lombok has various annotations to help developers with logging based on the logging
framework being used?

●​ @Slf4j will generate the below code and we can use the log variable directly.
●​ Generated java

// This code:

@Slf4j

public class LogExample {}

// will generate:

public class LogExample {

private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LogExample.class);

Java

●​ Similarly, using @CommonsLog, @Log4j2 will generate a log variable from the respective
library class.
Chapter 26: Externalized Configurations and Profiles
Properties Externalized Configurations
●​ Introduction:
○​ Spring Boot lets you externalize your configuration so that you can work with the
same application code in different environments. You can use a variety of external
configuration sources, including Java properties files, YAML files, environment
variables, and command-line arguments.
○​ By default, Spring Boot looks for the configurations or properties inside
application.properties/yaml present in the classpath location. But we can have
other property files as well and make SpringBoot to read from them.
●​
●​ Config/Properties Preferences:
○​ Spring Boot uses a very particular order that is designed to allow sensible overriding
of values. Properties are considered in the following order (with values from lower
items overriding earlier ones):
1.​ Properties present inside files like application.properties
2.​ OS Environmental variables
3.​ Java System properties (System.getProperties())
4.​ JNDI attributes from java:comp/env
5.​ ServletContext init parameters
6.​ ServletConfig init parameters
7.​ Command line arguments (Highest priority)
○​
●​

Reading Properties in Code

1. Reading properties with

We can read the properties/configurations defined inside a properties file with the help of the @Value
annotation like shown below. The @Value annotation leverages SpEL (Spring Expression Language)
expressions to read the configurations present inside the properties file.

●​ application.properties:

Generated properties

eazyschool.pageSize=10

eazyschool.contact.successMsg=Your message is submitted successfully.


Properties

Java Code:

Generated java

@Controller

public class DashboardController {

@Value("${eazyschool.pageSize}")

private int defaultPageSize;

@Value("${eazyschool.contact.successMsg}")

private String message;

// ...

Java

2. Reading properties using

Along with @Value, we can read the properties/configurations loaded with the help of the
Environment bean which is created by the Spring framework. Apart from user-defined properties,
using Environment we can read any environment-specific properties as well.
Generated java

@Controller

public class DashboardController {

@Autowired

private Environment environment;

public void logProperties() {

log.info(environment.getProperty("eazyschool.pageSize"));

log.info(environment.getProperty("eazyschool.contact.successMsg"));

log.info(environment.getProperty("JAVA_HOME")); // Reads environment variable

Java

3. Reading properties with

SpringBoot allows us to load all the properties which are logically together into a Java bean. For the
same, we can use the @ConfigurationProperties annotation on top of a Java bean by providing
the prefix value. We need to make sure to use the names inside the bean and properties file are the
same. Please follow the below steps to read the properties using @ConfigurationProperties.

●​ Step 1: We need to maintain the properties like below which have the same prefix like
'eazyschool'.
●​ Generated properties
eazyschool.pageSize=10

eazyschool.contact.pageSize=5

eazyschool.contact.successMsg=Your message is submitted successfully.

eazyschool.branches[0]=NewYork

eazyschool.branches[1]=Delhi

eazyschool.branches[2]=Paris

eazyschool.branches[3]=Singapore

Properties

●​ Step 2: Create a bean like below with all the required details.
●​ Generated java

@Component("eazySchoolProps")

@Data

@PropertySource("classpath:some.properties") // Optional: load from another file

@ConfigurationProperties(prefix = "eazyschool")

@Validated // Enable validation on properties

public class EazySchoolProps {

@Min(value=5, message="must be between 5 and 25")

@Max(value=25, message="must be between 5 and 25")

private int pageSize;


private Map<String, String> contact;

private List<String> branches;

Java

○​ @PropertySource can be used to mention the property file name if we are using
something other than application.properties.
○​ @ConfigurationProperties can be used to mention the prefix value that needs to
be considered while loading the properties into a given bean.
○​ @Validated can be used if we want to perform validations on the properties based
on the validation annotations mentioned on the field.
●​
●​ Step 3: Finally, we can inject the bean which we created in the previous step and start
reading the properties from it using Java style like shown below.
●​ Generated java

@Service

public class ContactService {

@Autowired

private EazySchoolProps eazySchoolProps;

public Page<Contact> findMsgsWithOpenStatus(...) {

int pageSize = eazySchoolProps.getPageSize();

// ...

Java
Reading properties with @PropertySource
Sometimes we maintain properties inside files which have a name not equal to
application.properties. In those scenarios, if we try to use the @Value, it will not work.

First, we need to communicate to SpringBoot about the property files with the below steps.​
We need to create a class with @Configuration and @PropertySource annotations like mentioned
below. Here we need to mention the property file name. Post these changes, we can refer to the
properties present inside the config.properties using the @Value annotation or Environment
bean. ignoreResourceNotFound = true will not throw an exception in case the file is missing.

Generated java

@Configuration

@PropertySource(value = "classpath:config.properties", ignoreResourceNotFound = true)

public class AppConfig {

Java

We can configure multiple property files as well using the @PropertySources annotation like
mentioned here.

Generated java

@Configuration

@PropertySources({

@PropertySource(value = "classpath:config.properties", ignoreResourceNotFound = true),

@PropertySource(value = "classpath:server.properties", ignoreResourceNotFound = true)

})
public class AppConfig {

Java

Profiles for Grouping Configurations


●​ Introduction:
○​ Spring provides a great tool for grouping configuration properties into so-called
profiles (dev, uat, prod) allowing us to activate a bunch of configurations based on
the active profile.
○​ Profiles are perfect for setting up our application for different environments, but
they're also being used in another use case like Bean creation based on a profile,
etc.
○​ So basically, a profile can influence the application properties loaded and beans
which are loaded into the Spring context.
●​
●​ Configuring Profiles:
○​ The default profile is always active. Spring Boot loads all properties in
application.properties into the default profile.
○​ We can create another profile by creating property files like below:
■​ application-prod.properties ------> for prod profile
■​ application-uat.properties ------> for uat profile
○​
○​ We can activate a specific profile using the spring.profiles.active property like
below:​
spring.profiles.active=prod

Quick Tip: Activating a Profile


Do you know there are many ways to activate a profile? Below are the most commonly used.

●​ By mentioning spring.profiles.active=prod inside the application.properties file.


●​ Using environment variables like below:​
export SPRING_PROFILES_ACTIVE=prod​
java -jar myApp-0.0.1-SNAPSHOT.jar
●​ Using a Java System property:​
java "-Dspring-boot.run.profiles=prod" -jar myApp-0.0.1-SNAPSHOT.jar​
mvn spring-boot:run "-Dspring-boot.run.profiles=prod"
●​ Activating a profile programmatically by invoking the method
setAdditionalProfiles("prod") inside the SpringApplication class.
●​ Using @ActiveProfiles while doing testing:​
@SpringBootTest​
@ActiveProfiles({"uat"})

Conditional Bean Creation using Profiles


With the help of Profiles, we can create a Bean conditionally. Below is an example where we can
create different beans based on the active profile.

Generated java

// This bean will be created for any profile EXCEPT "prod"

@Component

@Profile("!prod")

public class EazySchoolNonProdUsernamePwdAuthenticationProvider

implements AuthenticationProvider {

// ...

// This bean will be created ONLY when the "prod" profile is active

@Component

@Profile("prod")

public class EazySchoolUsernamePwdAuthenticationProvider

implements AuthenticationProvider {

// ...
}

Java

This is extremely useful for having different bean configurations for development (e.g., using an
in-memory database) and production (e.g., using a real database and a more secure authentication
provider).

Chapter 27: Monitoring with Spring Boot Actuator


Once an application is in production, it's critical to monitor its health, metrics, and internal state.
Spring Boot Actuator provides a wealth of production-ready features for this purpose.

Introduction to Spring Boot Actuator


●​ Actuator offers production-ready features such as monitoring and metrics to Spring Boot
applications. Actuator's features are provided by way of several endpoints, which are made
available over HTTP.
●​ To enable Actuator, we can mention the below dependency inside pom.xml.

Maven Dependency:

Generated xml

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-actuator</artifactId>

</dependency>

Xml

Key Concepts of Actuator:

●​ What is an Actuator? In a machine, an actuator is a component that's responsible for


controlling and moving a mechanism. In a Spring Boot application, the Spring Boot Actuator
plays that same role, enabling us to see inside a running application and, to some degree,
control how the application behaves.
●​ Endpoints: Using endpoints exposed by Actuator, we can know the following type of info:
○​ Health
○​ Configuration properties
○​ Logging levels
○​ Memory consumption
○​ Beans information
○​ Metrics etc.
●​
●​ Security: By default, Actuator doesn't expose many of the endpointsOf course. I will
continue generating the e-book with the final chapters on Spring Boot Actuator and AWS
Deployment, ensuring every slide, concept, and detail is fully included and explained without
omission.
Chapter 27: Monitoring with Spring Boot Actuator
Once your application is deployed, you need to monitor since they have sensitive information. We
can expose them using the below property:​
management.endpoints.web.exposure.include=*

●​ Checking Endpoints: Once the Actuator dependency is added to the project, we can
navigate to http://localhost:8080/actuator to check the list of APIs exposed by it.

API Paths Provided by Actuator


Below is a list of some of the most useful endpoints provided by Actuator.

HTTP method its health, Path Description


view its configuration,
understand its metrics, and
more. Spring Boot Actuator
provides production-ready
features to

GET /actuator/auditevent Produces a report of any


s audit events that have
been fired.

GET /actuator/conditions Describes all the beans in


the application context
and the conditions that
either passed or failed,
leading to the beans
created.

GET /actuator/configprop Describes all


s configuration properties
along with their current
values.

do just this.

Spring Boot Actuator


●​ Actuator offers production-ready features such as monitoring and metrics to Spring Boot
applications. Actuator's features are provided by way of several endpoints, which are made
available over HTTP.
●​ To enable Actuator, we can mention the below dependency inside pom.xml.

Maven Dependency:

Generated xml

<dependency>

<groupId>org.springframework.boot</groupId>

| GET | `/actuator/beans` | Describes all the beans in the application context. |

| GET, POST, DELETE | `/actuator/env` | Produces a report of all property sources and their properties
available to the Spring application. |

| GET | `/actuator/env/{to_match}` | Describes the value of a single environment property. |

| GET | `/actuator/heapdump` | s the heap dump. |

| GET | `/actuator/health` | Returns the aggregate health of the application and (possibly) the health of
external dependent applications. |

|<artifactId>spring-boot-starter-actuator</artifactId>

</dependency>

Xml
What is Actuator?

●​ Mechanism Controller: In a machine, an actuator is a component that's responsible for


controlling and moving a mechanism. In a Spring Boot application, the Spring Boot Actuator
plays that same role, enabling us to see inside a running application and, to some degree,
control how the application behaves.
●​ Exposed Endpoints: Using endpoints exposed by Actuator, we can know the following type
of info:
○​ ✓ Health
○​ GET | /actuator/httptrace | Produces a trace of the most recent 100 requests. |​
| GET | /actuator/info | Returns any developer-defined information about the
application. |​
| GET | /actuator/loggers | Produces a list of packages in the application along
with their configured and effective logging levels. |​
| GET, POST | /actuator/loggers/{name} | Returns the configured and effective
logging level of a given logger ✓ Configuration properties
○​ ✓ Logging levels
○​ ✓ Memory consumption
○​ ✓ Beans information
○​ ✓ Metrics etc.
●​
●​ Security: By default, Actuator doesn't expose many. The effective logging level can be set
with a POST request. |​
| GET | /actuator/mappings | Produces a report of all HTTP mappings and their
corresponding handler methods. |​
| GET | /actuator/scheduledtasks | Lists all scheduled tasks. |​
| GET | /actuator/threaddump | Returns a list of of the endpoints since they have sensitive
information. We can expose them using the below property:

Generated properties

# Expose all endpoints over the web

management.endpoints.web.exposure.include=*
Properties

●​
●​ Accessing Endpoints: Once the Actuator dependency is added to the project, we can
navigate to http://localhost: all application threads. | | GET
|/actuator/metrics| Returns a list of all metrics categories. | | GET
|/actuator/metrics/{name}` | Returns a multidimensional set of values for a given metric. |

**8080/actuator` to check the list of APIs exposed by it.

API Paths Provided by Actuator


Below is a list of some of the most useful endpoints provided by Actuator. The base path is
/actuator.

HTTP method Pat Descriptio


h n

GETSpring Boot Admin:**

We can use Spring Boot Admin, which is an administrative


frontend web application that makes Actuator endpoints more
consumable & readable by humans. It provides a nice UI to
visualize all the Actuator data.
Chapter 28: Deploying Spring Boot Applications to the
Cloud (AWS)
Finally, after building and testing our application, | /auditevents | Produces a report of any audit
events that have been fired. |​
| GET | /conditions | Produces a report of autoconfiguration conditions that either passed or failed,
leading to the beans created in the application context the last step is to deploy it to a production
environment so that users can access it. Cloud platforms like AWS are the most common choice for
deployment.
Deploying Spring Boot Applications
●​ Spring Boot's flexible packaging options provide a great deal of choice. |​
| GET | /configprops | Describes all configuration properties along with the current values.
|​
| GET | /beans | Describes all the beans in the application context. |​
| GET, POST, DELETE | /env | Produces a report of all property sources and their properties
available to the Spring application. |​
| GET | `/env/{ when it comes to deploying your application.
●​ You can deploy Spring Boot applications to a variety of cloud platforms, to virtual/real
machines.
●​ The SpringBoot Apps can be deployed to:
○​ Public Clouds like AWS, Azure, GCP
○​ Kubernetes
○​ Heroku
○​ toMatch} etc.
●​
●​ Since AWS is a famous cloud provider used by the majority of Organizations, let's explore
how to deploy a SpringBoot application using AWS.
●​ The common approaches to deploy a Spring Boot application inside AWS is using either
EC2 or Elastic Beanstalk.

AWS EC2 vs AWS Elastic Beanstalk requests. |


| GET | /info | Returns any developer-defined information about the application. |​
| GET | /loggers | Produces a list of packages in the application along with their configured and
effective logging levels. |​
| GET, POST | /loggers/{name} | Returns the configured and effective logging level of a given
logger. The effective
Amazon Elastic Compute Cloud (EC2) Elastic Beanstalk
(Recommended
Approach)

Amazon Elastic Compute Cloud (EC2) is a virtual cloud


infrastructure service offered by AWS that provides users
logging level can be set with a POST request.

GET /mappings

GET /scheduledtasks

GET /threaddump

GET /metrics

GET /metrics/{name}

Spring Boot Admin:​


We can use Spring Boot Admin, which is an administrative frontend web application that makes
Actuator endpoints more consumable & readable by humans. It provides a nice UI to view the
information from all balancing, and auto-scaling to application health monitoring. |​
| With EC2, we can deploy our Spring Boot application into AWS Cloud. But it is a traditional
approach and has many drawbacks. | With Beanstalk, Developers can ** your application's Actuator
endpoints.
Chapter 28: Deploying Spring Boot Applications to
thefocus on writing code instead of provisioning and
managing infrastructure**. |
| When we use EC2, the Developer or Organization need to take care of installing required
software, libraries like Java, Tomcat, and handle Auto scaling, load balancing, etc., manually. |
Elastic Beanstalk supports applications developed in Go, Java, .NET, Node.js, PHP, Python, and
Ruby. When you deploy your application, Elastic Beanstalk builds the selected supported platform
version and provisions Cloud (AWS)

The final step in the application lifecycle is deployment. We'll explore how to deploy our Spring Boot
application to the world's leading cloud provider, Amazon Web Services (AWS).

Deploying Spring Boot Applications


●​ Spring Boot's flexible packaging options provide a great deal of choice when it comes to
deploying your application.
●​ You can deploy Spring Boot applications to a variety of cloud platforms, to virtual/real
machines.
●​ The SpringBoot Apps can be deployed to:
○​ ✓ Public Clouds like AWS, Azure, GCP
○​ ✓ ** one or more AWS resources, such as Amazon EC2 instances, to run your
application. |
●​

AWS Elastic Beanstalk - How it works


The deployment process with Elastic Beanstalk is highly automated.

1.​ AWS Elastic Beanstalk: You start with the service to deploy and scale web applications and
services.
2.​ Use AWS Elastic BeanstalkKubernetes
○​ ✓ Heroku
○​ ✓ OpenShift etc.
3.​
●​ Since AWS is a famous cloud provider used by the majority of Organizations, let's explore
how to deploy a SpringBoot application using AWS.
●​ The common approaches to deploy a Spring Boot application inside AWS is using either EC
console: Create an AWS account and answer questions that become inputs to create your
environment.
1.​ Set up your application (with your code) and runtime (with Elastic Beanstalk platform):
You provide your application code (e.g., the executable JAR file) and choose the platform
(e.g., Java).
2.​ **Deploy environment:2 or Elastic Beanstalk.

AWS EC2 vs AWS Elastic Beanstalk

Amazon Elastic Compute Cloud (EC2) Elastic Beanstalk


(Recommended
Approach)

EC2 is a virtual cloud infrastructure service offered by AWS that


provides users with on-demand computing resources Launch
resources for your environment with AWS CloudFormation.
Elastic Beanstalk uses other AWS services to provision the
environment, including:

Generated code

* **AWS CloudFormation**

* **Amazon EC2 instances**

* through which users can create powerful servers in the cloud. Additionally, EC2 enables users to get a
virtual machine up and running in just a few clicks. | **Elastic Beanstalk** is a service for deploying and
scaling web applications and services. Upload your code and Elastic Beanstalk automatically handles the
deployment—from capacity provisioning, load balancing, and auto-scaling to application**S3 buckets**

* **Amazon CloudWatch**

1.​ Web application or service: The result is a fully deployed, running, and monitored web
application or service.
SOURCE: https://aws. health monitoring. |​
| With EC2, we can deploy our Spring Boot application into AWS Cloud. But it is a traditional
approach and has many drawbacks. | With Beanstalk, Developers can **focus on writing code
instead of provisioningamazon.com/elasticbeanstalk/

You might also like