Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Inside the Guide
This guide is a concise collection of spring boot knowledge we picked up over the years of Java development, the sections are divided up as follows:
Tutorials - longer format deep dives into topics
Recipes - quick code first solution oriented bites
Any feedback would be very much appreciated at book@katyella.com
If you like this content you may want to partner with us for your development needs
While our guide provides the some quick tips, sometimes achieving your project goals requires a bit more. That's where we come in. At Katyella, we provide US based staff augmentation. Whether you're looking to scale your team quickly or need specific expertise, we're here to help.
Interested in learning more about how we can help bring your projects to the next level? Visit us at katyella.com. Let's make your development journey a shared success.
Explanation:
In the code snippet above, the BookController
class includes:
Method getBookById
:
It uses the bookService
to attempt to find the book. If the book is not found, bookService.findBookById(id)
will return an empty Optional
, and the orElseThrow
method will throw a BookNotFoundException
.
The exception message includes the ID of the book that was not found, providing clarity in the logs and to the API users.
Exception Handler handleBookNotFoundException
:
This method remains the same as before. It handles the BookNotFoundException
by logging the exception details and returning a user-friendly message along with a 404 Not Found status.
By including the getBookById
method, we demonstrate a typical scenario in a RESTful service where a requested resource (in this case, a book) might not exist, leading to an exception that is gracefully handled by the handleBookNotFoundException
method.
This structure ensures that your REST API for book management not only handles requests but also deals with exceptions in a user-friendly manner, improving the reliability and usability of your service.
TL;DR:
The @Endpoint
annotation in Spring Boot Actuator allows for the creation of custom endpoints to expose specific application metrics or operations. In the provided code snippet, a custom endpoint activeSessions
is defined to report the current number of active sessions, leveraging the SessionRegistry
for real-time session tracking. This addition enhances monitoring capabilities by providing insights into user engagement and system load.
Detailed Explanation:
Purpose of @Endpoint: The @Endpoint
annotation marks a class as a custom Actuator endpoint, enabling it to expose specific data or operations through the Actuator framework. It's a powerful feature for extending the built-in monitoring and management capabilities of Spring Boot.
Exposing Active Sessions Count: The activeSessions
endpoint is designed to help monitor the current load on the application by reporting the number of active user sessions. This information is crucial for understanding user engagement and for scaling and performance tuning efforts.
Utilizing SessionRegistry: The SessionRegistry
is a Spring Security class that tracks all active sessions. By injecting SessionRegistry
into the custom endpoint, it's possible to count and report the number of active sessions in real-time.
Accessing the Custom Endpoint:
HTTP GET Request: http://localhost:8080/actuator/activeSessions
Response Example:
This response indicates the current number of active sessions, providing immediate insight into application usage.
Benefits:
Enhanced Monitoring: Adding a custom endpoint for active sessions directly addresses the need for real-time monitoring of user engagement, offering a clear view of the application's current load.
Operational Efficiency: With this endpoint, administrators and developers can quickly assess the application's performance and responsiveness, enabling proactive adjustments to infrastructure and application settings.
Strategic Decision Making: Information on active sessions aids in capacity planning and scalability strategies, ensuring that the application can handle peak loads efficiently.
By integrating a custom Actuator endpoint to monitor active sessions, Spring Boot applications can significantly improve their operational monitoring and management capabilities. This approach not only provides valuable insights into application performance but also supports informed decision-making for enhancing user experience and system reliability.
Lombok is a Java library that helps reduce boilerplate code, particularly useful in Spring applications for creating cleaner, more maintainable codebases.
Lombok's @RequiredArgsConstructor
, @Slf4j
, and @Data
annotations significantly reduce the boilerplate code in Spring services and entities. @RequiredArgsConstructor
automatically generates a constructor for dependency injection, @Slf4j
provides a logging capability, and @Data
bundles common methods like getters, setters, and toString()
for data classes.
Before Lombok
Traditionally, a Spring service with a dependency on a repository and a simple data class might look like this:
After Lombok
With Lombok, the same functionality is achieved with less code:
@RequiredArgsConstructor eliminates the need for an explicit constructor.
@Slf4j automatically injects a log
instance, removing the manual logger declaration.
@Data generates getters, setters, toString()
, equals()
, and hashCode()
methods for the User
class.
Additional Lombok Features Demonstrated
@Slf4j: Simplifies logging throughout the application. Just use log.info()
, log.debug()
, etc., without manually setting up the logger.
@Data: Ideal for data classes or entities. It not only includes getters and setters but also essential methods like toString()
, making object debugging and logging more straightforward.
Benefits of Using Lombok in Spring Services
Reduced Boilerplate Code: Lombok annotations decrease the amount of manually written code, making the service and model layers cleaner and more readable.
Enhanced Maintainability: Less code means easier maintenance and fewer places for bugs to hide.
Improved Development Speed: Developers can focus on business logic rather than writing and maintaining repetitive code structures.
Integrating Lombok into Spring Boot applications offers a clear path to more concise, maintainable code. By automating common coding tasks, Lombok allows developers to concentrate on what's truly important, enhancing productivity and the overall quality of the code. Whether you're building complex business services or simple data classes, Lombok's suite of annotations can significantly streamline your development process.
JSON Web Tokens (JWTs) are a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret or a public/private key pair using the RSA or ECDSA algorithms.
Authentication is the act of validating that users are who they claim to be using passwords, biometrics, one-time passwords, and more. JWTs can be used to establish the identity of users. When a user logs in, the server generates a JWT that encodes a set of claims. The server then returns this token to the client, and the client sends the token back with each request. The server verifies the token and only allows the request to proceed if the token is valid.
Authorization is the process by which individual users or groups of users are granted access to a specific resource or function. JWTs also play a crucial role in authorization, as they enable servers to know what resources a user can have access to. When the JWT includes claims like user roles or permissions, the server can determine whether to grant or deny access to certain operations or resources.
Securing APIs and managing access control are critical in web applications. JWTs help in securing APIs by allowing the server to process requests without having to look up too much user information since the token is self-contained. The authorization process using JWTs enables role-based access control, ensuring that users only have access to the resources and operations their role permits.
Overall, JWTs are integral to the security architecture of modern web applications, enabling reliable authentication and fine-grained authorization while also being lightweight and easily transmittable over the web.
In this project, we are going to employ several key tools and technologies:
Java Spring Boot: An open-source Java-based framework used to create a microservice. It is easy to create stand-alone, production-grade Spring based applications with minimal effort.
H2 Database: A lightweight in-memory database that can be embedded in Java applications or run in the client-server mode. It's perfect for development and testing since it doesn't require any setup like a regular database.
JSON Web Tokens (JWT): A standard token format used in authentication and authorizations that allows you to securely transmit data between parties as a JSON object.
Select Maven
as your Project Type and Java as your language
Choose your desired version of Spring Boot. This tutorial uses 3.2.2.
Provide the Project Metadata for your project.
Choose Jar
as the packaging option.
Select the version of Java you are using. This tutorial uses 21.
Click on "Generate" to download the project template.
Once downloaded and extracted, create the following Java Packages in your <ArtifactId> folder (com
in this diagram)
To include the necessary dependencies for Spring Security, the H2 database, and JWT, open the pom.xml
file and add the following dependencies:
Make sure to consult the official documentation for up-to-date version numbers.
We will need to configure a few properties related to our H2 database, logging, JWT authentication, and authorization. Here are the settings used in our project:
To secure our web application, we implement Spring Security by defining a SecurityConfig
class. This class utilizes a combination of annotations to integrate Spring Security with our project effectively.
Below is an overview of what the SecurityConfig
class accomplishes:
@EnableWebSecurity
: This annotation activates Spring Security’s web security support and provides the Spring MVC integration. It also extends the Spring Security configuration using HTTPSecurity.
@EnableMethodSecurity
: This enables method-level security based on annotations. It allows us to secure methods in our controllers by specifying roles and conditions for access.
@Configuration
: The class is marked as a configuration class, and it defines Spring Beans.
@RequiredArgsConstructor
: This is a Lombok annotation that generates a constructor with required dependencies (in this case, JwtAuthenticationFilter, UserService, and PasswordEncoder).
The SecurityConfig
includes the following Beans:
AuthenticationProvider
: This Bean sets the custom user details service and password encoder. It is necessary for authenticating users based on username and password.
AuthenticationManager
: This Bean gets the authentication manager from Spring Security's AuthenticationConfiguration which in turns help manage the authentication procedure.
SecurityFilterChain
: This crucial Bean within the Spring Security filter chain, allows us to configure the rules and conditions for security along different paths in our application. We disable CSRF protection as it is not needed for REST APIs and define stateless session management. Furthermore, we specify URL patterns and the associated security settings, permitting everyone to access the signup and signin endpoints, whereas protecting all other endpoints. Finally, we ensure that the JWT filter is applied before the UsernamePasswordAuthenticationFilter to extract and verify the token.
Here's how we set up the SecurityConfig
:
This setup is pivotal for JWT-based authentication, ensuring that each request is properly authenticated and has the correct authority to access various resources.
JWT Authentication is a key aspect in securing web applications by validating the identity of users via tokens. Let's dive into how the JwtService
class in the provided code snippet helps in implementing JWT authentication:
The JwtService
class performs several essential actions:
Token Generation: generateToken(UserDetails userDetails)
method generates a new token for a user based on their UserDetails
. It includes the user's username as the subject, current time as the issue time, and sets the expiration time based on the jwtExpirationMs
value.
Token Validation: isTokenValid(String token, UserDetails userDetails)
method checks if the token is valid by comparing the username in the token with the username in the passed UserDetails
, and also checks if the token has expired.
Extract Claims: Various methods such as extractAllClaims
, extractExpiration
, and extractUserName
help in retrieving specific claims from the token like subject (username) and expiration.
Secret Key: It uses the jwtSecretKey
declared in application.properties
to decode and sign the tokens.
Here's the entire JwtService
code for clarity:
This code snippet is a sample controller which demonstrates how role-based access-control can be implemented using Spring Security's @PreAuthorize
annotation. It provides access to different REST endpoints based on the authenticated user's role.
GET /api/v1/test/users
is secured with @PreAuthorize("hasRole('USER')")
, restricting access to this endpoint to only allow authenticated users with the USER
role.
GET /api/v1/test/admins
is annotated with @PreAuthorize("hasRole('ADMIN')")
, restricting access to this endpoint to only allow authenticated users with the ADMIN
role.
GET /api/v1/test/role_dependent
programmatically checks the user's role without using @PreAuthorize
, offering a more dynamic approach to role-based access-control.
A more robust BookController
that interfaces with our database will be implemented in the next step. This BookController
will use these principles of role-based authorization to manage access to book-related resources.
Now that we have set up our project, implemented JWT-based authentication and role-based authorization, let's build the Books API to manage book-related resources. We'll create a BookController
to handle CRUD (Create, Read, Update, Delete) operations on books.
Now we can start our application, sign up and sign with a new user or sign in with a pre-populated one, and use the JWT returned from that request to access our various endpoints according to the role assigned to our user!
Here's how we can sign up our user:
Or log in with our admin user:
Experiment with different calls with different roles to see how our application restricts what data is available to a user based on their role. Check the /src/test
directory in the repo to view some example API calls. You will need to provide the JWT token fetched in the /signin
step, and remember, it expires after 1 hour!
Here's an example of accessing the user-restricted endpoint as a user role:
Properties File (application.properties
)
Spring Component
In Spring Boot applications, you can easily externalize configuration and access properties in your components. The above code demonstrates how you can define properties in the application.properties
file and inject them into your Spring components using the @Value
annotation.
Explanation:
Properties File (application.properties
):
Contains key-value pairs defining properties and their values.
bookstore.name
and bookstore.location
are the properties used in this example.
Spring Component (BookstoreProperties
):
Annotated with @Component
, making it a Spring-managed bean.
Uses the @Value
annotation to inject property values from the application.properties
file.
The syntax ${property.name}
within @Value
specifies the property to be injected.
printBookstoreDetails
is a method to display the injected values, demonstrating that the values have been successfully injected.
Benefits:
Decoupling of Configuration and Code: Enables changing the application's behavior without code changes by modifying the properties file.
Ease of Maintenance: Centralizes configuration management, simplifying changes and management.
Flexibility for Different Environments: Allows for different configurations in development, testing, and production environments without changing the codebase.
By leveraging the @Value
annotation in Spring Boot, you can maintain clean separation between your configuration and code, making your application more adaptable and easier to manage across different environments.
Spring Boot application for web scraping with JSoup
Setup: This Spring Boot application is configured to perform web scraping tasks using JSoup. It includes the @EnableScheduling
annotation to enable scheduled tasks.
Scheduled Task: The WebScrapingService
class contains a method scrapeWebsite
annotated with @Scheduled
, set to execute every 10 seconds. This method uses JSoup to connect to a specified URL, retrieves the document, and prints the website's title.
Running: Upon running the application, the scheduled task will automatically scrape the website at the defined interval, demonstrating a basic use case of web scraping in a Spring Boot application.
This example provides a streamlined approach to integrating web scraping capabilities into a Spring Boot application, showcasing the ease of setting up scheduled tasks with JSoup for HTML parsing.
Explanation:
In RESTful services, ensuring consistent and user-friendly error responses across the entire application is crucial. The @ControllerAdvice
annotation in Spring Boot allows you to handle exceptions globally, avoiding the need to declare exception handlers in each controller. Just by having a class with this annotation in the class path. Here's how you can implement global exception handling for a book management RESTful service:
Code Implementation:
Annotation @ControllerAdvice
: This annotation declares the class as a global exception handler, making the exception handling methods within applicable across all controllers.
Method handleBookNotFoundException
:
This method is marked with @ExceptionHandler(BookNotFoundException.class)
, indicating it handles exceptions of type BookNotFoundException
.
The @ResponseStatus(HttpStatus.NOT_FOUND)
annotation ensures that a 404 Not Found status is returned.
The method logs the exception details and returns a ResponseEntity
with a user-friendly error message.
Benefits of Using @ControllerAdvice
:
Consistency: Ensures that the same exception across different controllers results in a consistent response.
Cleaner Code: Reduces duplication by avoiding repetitive exception handlers in each controller.
Centralized Configuration: Makes it easier to manage and maintain exception handling logic in one place.
By leveraging @ControllerAdvice
or @RestControllerAdvice
(for RESTful services where the response body is always expected), you can efficiently manage exceptions globally, ensuring that your RESTful service is robust, consistent, and user-friendly.
This approach to handling exceptions globally streamlines your error management strategy, making your RESTful service more maintainable and your error responses more predictable and informative.
Effectively use Lombok for logging while cautiously managing entities with lazy-loaded relationships to circumvent potential performance issues.
Service Class with Lombok Logging:
Log Output Example:
TL;DR:
Utilizing Lombok's @ToString
annotation with the exclude
attribute allows for the exclusion of lazy-loaded fields, such as orders
in a User
entity, preventing unintended loading during logging. The @Slf4j
annotation simplifies logging setup, enabling efficient and safe logging practices in Spring applications. This approach ensures that entity logging is both informative and performance-conscious.
Detailed Explanation:
Before Lombok Integration:
Traditionally, logging entity information and managing lazy-loaded relationships required manually writing boilerplate code for logging setup and toString()
methods, carefully avoiding lazy-loaded fields to prevent performance issues.
After Lombok Integration:
Simplified Logging: The @Slf4j
annotation automatically injects a logger, reducing the boilerplate code for logger initialization.
Safe toString()
Implementation: The @ToString(exclude = "orders")
annotation generates a toString()
method that excludes specified lazy-loaded fields, mitigating the risk of accidentally triggering database queries for uninitialized data.
Benefits of This Approach:
Performance Optimization: By excluding lazy-loaded fields from the toString()
method, applications avoid unnecessary database hits and potential LazyInitializationException
errors outside of transactional contexts.
Enhanced Maintainability: Reducing boilerplate code for logging and toString()
methods makes the codebase cleaner and easier to maintain.
Improved Developer Productivity: Developers can focus on business logic rather than worrying about logging setup and the intricacies of lazy loading with Hibernate.
Lombok provides powerful annotations like @Slf4j
and @ToString
that simplify logging and toString implementations in Spring applications. When working with Hibernate entities, especially those with lazy-loaded relationships, Lombok's features help maintain performance and prevent common issues associated with lazy loading. This approach allows developers to enjoy the benefits of concise code and efficient logging while ensuring application performance remains optimal.
In this quick recipe, we'll explore the proper and improper ways to implement CRUD (Create, Read, Update, Delete) operations in RESTful services
Proper Way to Implement CRUD in Spring Boot
Service Layer:
Assuming basic CRUD operations are defined in the UserService
.
Improper Way to Implement CRUD in Spring Boot
Controller:
HTTP Methods: The proper implementation uses HTTP methods according to REST principles (POST
for creation, GET
for reading, PUT
for updating, and DELETE
for deletion). The improper way misuses HTTP methods, leading to semantic confusion and potential security risks.
URI Structure: A RESTful URI should be descriptive and hierarchical, focusing on resources rather than actions. The proper example uses /api/users
with resource IDs for specific operations, while the improper example uses action-based URIs like /createUser
, which is not recommended.
Response Entity: The proper way uses ResponseEntity
to provide more control over the HTTP response, allowing for the setting of status codes and headers. The improper way returns the model directly, limiting the ability to respond with appropriate HTTP status codes or headers.
This recipe highlights the importance of adhering to RESTful principles and Spring Boot best practices to create clear, efficient, and maintainable API endpoints.
TL;DR:
The @TestConfiguration
annotation allows for the customization of Spring Boot's application context for testing purposes, without affecting the main application configuration. In the provided code snippet, @TestConfiguration
is used to define a SecurityFilterChain
bean that disables OAuth2 security, simplifying integration testing by bypassing authentication and authorization steps.
Explanation:
Purpose of @TestConfiguration: This specialized configuration annotation is designed for use in tests, enabling developers to override or add additional configuration without influencing the main application context. It's particularly useful for setting up or mocking certain behaviors specific to testing scenarios.
Disabling OAuth2 Security: For integration tests, particularly those not focusing on security, it's often practical to bypass security constraints to directly test business logic and integration points. The provided SecurityFilterChain
bean method disables CSRF protection and configures Spring Security to permit all requests, effectively neutralizing OAuth2 security for tests.
Integration with Test Classes: To apply this configuration, the @Import(IntegrationTestConfig.class)
annotation should be added to your test classes. This ensures that the test context includes the overridden security configuration, allowing tests to run without the need for authenticating requests.
Benefits:
Simplified Testing Environment: By disabling security features that are not relevant to certain integration tests, developers can focus on the functionality and integration aspects of the application.
Isolated Configuration: Since @TestConfiguration
is only applied to tests where it's explicitly imported, there's no risk of it affecting the production configuration or other tests.
Flexibility and Control: This approach provides fine-grained control over the test environment, allowing for more accurate and efficient testing of specific components or functionalities.
By strategically using @TestConfiguration
to disable OAuth2 security, developers can ensure that their integration tests are both effective and efficient, focusing on the core functionality of the application under test.
Visit the .