Spring, SpringBoot, JPA, Security, Hibernate - Zero To Master
Spring, SpringBoot, JPA, Security, Hibernate - Zero To Master
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 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
● 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.
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.
● 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 (originally J2EE) was the official specification for building enterprise applications. Its
timeline shows a steady but sometimes slow evolution:
● 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)
● 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
Generated java
Generated java
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.
● 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 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.
Responsibilities:
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.
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.
Generated java
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.
Generated java
Console Output:
Generated code
Solving NoUniqueBeanDefinitionException
To avoid this exception, you must be more specific when asking for a bean.
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
Java
Output on Console:
Generated code
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() { /* ... */ }
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 */ }
}
Java
Output on Console:
Generated code
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
Configuration Class:
Generated java
@Configuration
@ComponentScan(basePackages = "com.example.beans") // Tell Spring to scan this package
public class ProjectConfig {
}
Java
Main Application:
Generated java
Java
Output on Console:
Generated code
● @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.
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.
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.
Example:
Generated java
@Component
public class Vehicle {
@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
This method gives us the power to dynamically add beans to the context after the context has
already been initialized.
The
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.
We can use this method inside conditional logic to register different beans based on runtime
conditions.
Generated java
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.
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 (
Generated xml
</beans>
Xml
Generated java
Java
Output on Console:
Generated code
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.
● 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.
Generated java
Java
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.
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.
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
Java
Output on Console:
Generated code
● 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.
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;
}
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.
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.
Generated java
@Component
public class Vehicle {
private String name = "Toyota";
//...getter
}
@Component
public class Person {
private String name = "Lucy";
//...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.
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.
Generated java
@Component
public class Person {
private String name = "Lucy";
private Vehicle vehicle;
@Autowired
public void setVehicle(Vehicle vehicle) {
this.vehicle = vehicle;
}
//...getters
}
Java
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!
//...getters
}
Java
● 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.
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
@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.
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
@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
@Component
public class Person {
private String name = "Lucy";
private final Vehicle vehicle;
Java
This is the most explicit way to resolve ambiguity. Spring will inject the bean named vehicle2,
ignoring all other rules.
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.
Generated java
@Component
public class Person {
private String name = "Lucy";
private Vehicle vehicle;
@Component
public class Vehicle {
private String name;
private Person person;
Java
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.
The Scenario:
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.
1. Singleton
2. Prototype
3. Request
4. Session
5. Application
Generated java
@Component
// This is redundant as Singleton is the default, but shown for clarity.
@Scope(BeanDefinition.SCOPE_SINGLETON)
public class VehicleServices {
// ...
}
Java
Output on Console:
Generated code
The output is true because both variables vehicleServices1 and vehicleServices2 refer to the
exact same bean instance inside the Spring context.
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
Java
Now, consider two concurrent users (Thread 1 and Thread 2) trying to reserve "table1":
The result is a corrupt state. Both users think they have the table, but only USER1's reservation is
actually stored.
● 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.
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
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.
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);
Java
Output on Console:
Generated code
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
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.
● 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.
Generated java
if(started) {
status = tyres.rotate(); // <-- The *actual* business logic
} else {
logger.log(Level.SEVERE, "Vehicle not started to perform the operation");
}
Java
There is so much non-business logic code along with the main business logic.
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
Java
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.
● 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.
"A Developer wants some logic to be executed before each execution of the method playMusic()
present inside the bean VehicleServices."
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.
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.
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 {
// After logic
System.out.println("Method executed");
}
}
Java
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
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 {
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.
Web Clients: These are the applications that users interact with.
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.
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.
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.
● 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.
● 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.
● 😞 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):
● 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.
●
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 {
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.
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.
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
● 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.
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.
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
● @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.
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)
●
org.hibernate.validator.constraints.*
Validation in Practice
Step 1: Declare Validations in a POJO class.
Generated java
@Data
public class Contact {
Java
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
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.
1. Singleton
2. Prototype
3. Request
4. Session
5. Application
● 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.
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
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.
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.
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
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.
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
Generated java
@Configuration
public class ProjectSecurityConfig {
@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.
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.
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.
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.
Generated html
Html
Step 3: Use any of the Thymeleaf Security tags inside the HTML code based on
Authentication details.
● 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.
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>
Html
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.
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
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.
Generated html
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.
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.
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.
Maven Dependency:
Generated xml
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
Xml
● 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.
● 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.
Generated java
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.
Maven Dependency:
Generated xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
Xml
● 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.
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).
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 {
@Autowired
public PersonDAOImpl(DataSource dataSource) {
this.jdbcTemplate = new JdbcTemplate(dataSource);
}
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).
Java
Generated java
Java
Generated java
Sample usage of
"Jon", "Doe");
Java
Generated java
"Maria", 5276L);
Java
Generated java
Java
Other
● You can use the execute(..) method to run any arbitrary SQL. Consequently, the method
is often used for DDL statements.
Generated java
Java
Generated java
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
Java
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;
Java
This is much more readable than using ? when you have many parameters.
Generated java
@Repository
public class HolidaysRepository {
@Autowired
public HolidaysRepository(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
Java
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.
● : 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.
This layered approach allows you to choose exactly the level of functionality you need for your
repository.
● 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.
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
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;
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
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;
Java
The save() method is provided for free by extending CrudRepository.
● 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.
Generated java
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.
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).
findByFirstnameEquals ?1
Between findByStartDateBetween ... where
x.startDate
between ?1 and
?2
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.
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 {
@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
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
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.
Generated properties
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.
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
This class contains the actual business logic for the validation.
Generated java
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
Java
● 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
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.
Generated java
@Component
@Override
} else {
@Override
Java
This provider is registered as a component, so Spring will automatically pick it up and use it in the
authentication process.
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.
● 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.
●
Different Implementations of
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.
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
● 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
Java
EazyClass Entity (
Generated java
@Entity
@Table(name = "class")
Java
● 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.
Generated java
@Entity
// ...
@JoinTable(name = "person_courses",
joinColumns = {
},
inverseJoinColumns = {
})
Java
Courses Entity (The other side):
Generated java
@Entity
// ...
Java
Let's assume we have the tables and records below which have a many-many relationship between
them.
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.
● 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.
Generated java
// 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.
Generated java
// 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);
● 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
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 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
Sorting Examples
Here are various ways to implement sorting with custom queries.
Generated java
// Method name defines the sorting order
List<Person> findByNameOrderByAgeDesc(String name);
Generated java
Generated java
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 {
// ...
}
Generated java
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
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.
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.
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 {
// ...
}
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
ContactRepository contactRepository;
@GetMapping("/getMessagesByStatus")
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.
This is a convenience annotation that marks the class as a controller where every method inherits
the @ResponseBody behavior.
Example with
Generated java
@Autowired
ContactRepository contactRepository;
@GetMapping("/getMessagesByStatus")
return contactRepository.findByStatus(status);
Java
Annotation/Class Description
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
●
● @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")
Java
@JsonIgnore
Java
● We can also use @JsonIgnoreProperties(value = { "createdAt" }) on top of a
POJO class if we want to mention multiple fields.
Generated java
configuration = ProjectConfiguration.class)
Java
Step 2: If we need to send the authentication details, then create a bean with the required
details.
Generated java
@Bean
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")
return contactProxy.getMessagesByStatus(status);
Java
Generated java
@Bean
Java
Step 2: Using
Generated java
@Autowired
RestTemplate restTemplate;
@PostMapping("/saveMsg")
headers.add("invocationFrom", "RestTemplate");
Java
Generated java
@Bean
return WebClient.builder()
.filter(ExchangeFilterFunctions
.basicAuthentication("[email protected]", "admin"))
.build();
Java
Step 2: Using
Generated java
@Autowired
WebClient webClient;
@PostMapping("/saveMessage")
return webClient.post().uri(uri)
.header("invocationFrom", "WebClient")
.body(Mono.just(contact), Contact.class)
.retrieve()
.bodyToMono(Response.class);
Java
Maven Dependency:
Generated xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-rest</artifactId>
</dependency>
Xml
● 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
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")
// ...
Java
Generated java
@RepositoryRestResource(exported = false)
// ...
}
Java
● 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
logging.level.root=INFO
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
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
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.
● @Slf4j will generate the below code and we can use the log variable directly.
● Generated java
// This code:
@Slf4j
// will generate:
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)
○
●
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
Java Code:
Generated java
@Controller
@Value("${eazyschool.pageSize}")
@Value("${eazyschool.contact.successMsg}")
// ...
Java
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
@Autowired
log.info(environment.getProperty("eazyschool.pageSize"));
log.info(environment.getProperty("eazyschool.contact.successMsg"));
Java
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.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
@ConfigurationProperties(prefix = "eazyschool")
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
@Autowired
// ...
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
Java
We can configure multiple property files as well using the @PropertySources annotation like
mentioned here.
Generated java
@Configuration
@PropertySources({
})
public class AppConfig {
Java
Generated java
@Component
@Profile("!prod")
implements AuthenticationProvider {
// ...
// This bean will be created ONLY when the "prod" profile is active
@Component
@Profile("prod")
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).
Maven Dependency:
Generated xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
Xml
● 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.
do just this.
Maven Dependency:
Generated xml
<dependency>
<groupId>org.springframework.boot</groupId>
| GET, POST, DELETE | `/actuator/env` | Produces a report of all property sources and their properties
available to the Spring application. |
| 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?
Generated properties
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. |
GET /mappings
GET /scheduledtasks
GET /threaddump
GET /metrics
GET /metrics/{name}
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).
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.
Generated code
* **AWS CloudFormation**
* 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/