Unit 2
Unit 2
3. Modularity: Allows for easy updates to cross-cutting concerns without affecting core logic.
4. Aspect as a Unit: Unlike OOP, where classes are the primary unit of modularity, AOP uses
Aspects as modular units.
Term Description
A specific point in the application, like method execution, where cross-cutting concerns
Joinpoint
can be applied.
Advice Code that implements cross-cutting concerns, executed at specific join points.
An expression that determines where advice should be applied (e.g., before or after
Pointcut
specific methods).
Types of Advice
After Executes after the join point, regardless of the outcome (like a finally block).
Around Wraps the join point with logic before and after its execution.
1. @Aspect
Declares a class as an aspect.
2. @Before, @After, @AfterReturning, @AfterThrowing, @Around
Specify when advice should run relative to the join point.
3. @Pointcut
Declares reusable pointcut expressions.
Pointcut Syntax
General Form:
php
Copy code
Explanation:
o <return-type>: The return type of the method (* for any return type).
AOP (Aspect-Oriented Programming) allows you to define cross-cutting concerns (like logging,
security, transaction management) separately from the core business logic. Below are examples of
various types of AOP advices that can be used in Spring to log actions or handle exceptions, typically
used in services like fetchCustomer() from a service class such as CustomerServiceImpl.
1. Before Advice
java
Copy code
@Aspect
@Component
@Before("execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
public void logBeforeAdvice(JoinPoint joinPoint) {
Explanation:
Logging the current time: A timestamp for when the report is generated.
2. After Advice
After advice is executed after the method execution, regardless of whether it throws an exception or
not. It is useful for cleanup tasks like closing resources.
java
Copy code
@Aspect
@Component
@After("execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
Explanation:
@After: This advice is executed after fetchCustomer() finishes execution, even if an exception
occurs.
It logs the time of the report generation after the method has executed.
This advice is executed only when the method successfully completes without throwing an
exception. It is typically used to log results or process return values.
java
Copy code
@Aspect
@Component
@AfterReturning(pointcut = "execution(*
com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
You can access the value returned by the method using the returning attribute.
java
Copy code
@Aspect
@Component
@AfterReturning(pointcut = "execution(*
com.infy.service.CustomerServiceImpl.fetchCustomer(..))", returning = "result")
Explanation:
returning = "result": The result variable holds the value returned by fetchCustomer().
This advice is executed only if the target method throws an exception. It's useful for logging or
handling exceptions that occur in the method.
java
Copy code
@Aspect
@Component
@AfterThrowing("execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
java
Copy code
@Aspect
@Component
@AfterThrowing(pointcut = "execution(*
com.infy.service.CustomerServiceImpl.fetchCustomer(..))", throwing = "exception")
public void logAfterThrowingAdvice(JoinPoint joinPoint, Exception exception) {
Explanation:
throwing = "exception": The exception variable contains the exception thrown by the
method.
Executed after the target method finishes, regardless of the outcome (success or
After Advice
exception).
After Executed after the method successfully returns, used for logging return values or
Returning post-processing.
After Throwing Executed if the target method throws an exception, useful for error logging.
These advices provide a powerful way to modularize the handling of cross-cutting concerns such as
logging, exception handling, and resource management, all while keeping the core business logic
intact.
Adopting best practices in the design and development of Spring Boot applications ensures better
maintainability, performance, security, and testability. Here are the key best practices for quality and
security in Spring Boot development.
The simplest and most effective way to create a Spring Boot project is by using Spring Initializr. It
provides a user-friendly interface to generate a production-ready application. This can be directly
imported into IDEs like Spring Tool Suite (STS) or Eclipse for further development.
Advantages:
A well-structured project helps in maintaining a clean, organized, and scalable application. There are
two popular approaches for project structuring in Spring Boot applications:
First Approach:
Group related classes (controllers, services, etc.) by the domain. For example:
Second Approach:
Do not place classes in the default package, as this can cause issues with Spring Boot's
@ComponentScan and @SpringBootApplication annotations. Use proper package naming
conventions like com.infy.service or com.infy.controller.
In Spring, there are multiple ways to inject dependencies into beans, but constructor injection is
recommended for mandatory dependencies because:
It improves testability and ensures that all required dependencies are provided at the time of
object creation.
It avoids the problem of uninitialized fields that can occur with field injection.
Example:
java
Copy code
@Autowired
this.customerRepository = customerRepository;
}
Setter Injection can be used for optional dependencies, but constructor injection is generally
the best practice for required dependencies.
Domain classes (i.e., entities) should not use Spring's stereotype annotations like @Component,
@Service, or @Repository as it can lead to unnecessary bean creation. These annotations are
typically reserved for business logic layers and services, not for domain models.
Example (incorrect):
java
Copy code
@Component
Instead, let Spring manage beans in the application layer (@Service, @Controller, etc.).
When defining beans, it's essential to choose the correct scope for the bean:
Singleton Scope (default): Use for stateless beans where the state doesn’t change between
requests.
Prototype Scope: Use for stateful beans that need a new instance each time they are
requested.
By default, Spring scans all classes in the package where the application starts
(@SpringBootApplication). However, if you only need to scan a specific package, specify that package
explicitly to avoid unnecessary bean scanning.
Example:
java
Copy code
@Configuration
To make the application more maintainable and testable, use the @Autowired annotation on
constructors instead of fields or setter methods. Constructor injection is preferred because it ensures
that dependencies are immutable and provides better testability.
java
Copy code
@Autowired
this.myRepository = myRepository;
For cleaner and more maintainable Aspect-Oriented Programming (AOP), store all your Pointcut
expressions in a common class. This allows easier maintenance and reusability of pointcuts in
multiple aspects.
Example:
java
Copy code
@Pointcut("execution(* com.infy.service.CustomerServiceImpl.fetchCustomer(..))")
java
Copy code
@Around("com.infy.service.CustomerServiceImpl.fetchCustomer.aspect.CommonPointConfig.logBefo
reAdvice()")
// Logic
}
9. Proper Error Handling with Exception Handling Advice
In any application, effective error handling is crucial for robustness. Spring allows handling exceptions
globally using @ControllerAdvice and @ExceptionHandler. It's a good practice to centralize exception
handling in a dedicated class.
Example:
java
Copy code
@ControllerAdvice
@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorDetails>
handleResourceNotFoundException(ResourceNotFoundException ex) {
Security is a critical aspect of any application, and Spring Boot provides numerous security features.
Follow these best practices to ensure your Spring Boot application is secure:
Use CSRF protection in web applications, even if it's a stateless API, with tokens like JWT for
better security.
Testing is an essential part of any Spring Boot application. Use the following best practices for
effective testing:
Use unit tests for individual methods or components (using @Test).
Use integration tests to test the interaction between components and the database.
Mock external services and API calls with tools like Mockito.
Follow the Arrange-Act-Assert pattern in tests to keep them clean and understandable.
java
Copy code
@RunWith(SpringRunner.class)
@SpringBootTest
@Autowired
@MockBean
@Test
Mockito.when(customerRepository.findById(1L)).thenReturn(Optional.of(mockCustomer));
assertNotNull(customer);
assertEquals("John", customer.getFirstName());
As a best practice, always use constructor-based dependency injection for mandatory dependencies
and setter-based injection for optional ones. This helps in ensuring that objects are fully initialized
when created, making the code more predictable and easier to test.
Conclusion
By following these best practices, you can build clean, secure, scalable, and maintainable Spring
Boot applications. These practices help in better application design, easier testing, improved
performance, and enhanced security. They are essential for developing high-quality applications that
can be efficiently maintained and extended over time.