
Play Store Application link β Spring Framework in 9 steps – Apps on Google Play
Understanding AOP (Aspect-Oriented Programming) in Spring
Github project link (Xml based) – https://github.com/kuldeep101990/SpringAOPXml
Github project link (Annotation based) – https://github.com/kuldeep101990/SpringAOPAnnotation
1- Aspect
An Aspect is a class that defines common behaviors (advices) to be applied at specific points (join points) in your application. It can be configured via XML or using Springβs AspectJ integration.
2- Join Point
A Join Point is a specific point in the execution of your application where an aspect can be applied. Spring primarily supports method execution as join points.
Example:
public class UserServiceImpl implements UserService {
public void addUser(User user) {
// Implementation code to add a user
}
}
// Here, the execution of addUser() is a join point where an aspect can be applied.
3- Advice
Advice is the action taken at a join point. It can run before, after, or around method execution.
Example of Before Advice:
public class LoggingAspect {
@Before("execution(* com.example.service.*.*(..))") // Match any method in service package
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before " + joinPoint.getSignature().getName() + " method is called");
}
}
// logBefore() logs a message before the matched method executes.
4- Target Object
The Target Object is the object being advised by aspects. In Spring, this is usually a proxy object.
Example:
@Configuration
@EnableAspectJAutoProxy // Enable AspectJ proxying
public class AppConfig {
@Bean
public LoggingAspect loggingAspect() {
return new LoggingAspect();
}
@Bean
public UserService userService() {
return new UserServiceImpl(); // Target object
}
}
5- Weaving
Weaving is the process of linking aspects with target objects to create advised proxy objects. This occurs at runtime in Spring.
Example of Weaving:
// Same AppConfig as before enables weaving of aspects with the UserService
Types of Advice
- Before Advice:
@Before("execution(* com.example.UserService.addUser(..))")
public void beforeAddUserAdvice() {
System.out.println("Before adding a user...");
}
- After Returning Advice:
@AfterReturning("execution(* com.example.UserService.getUser(..))")
public void afterGetUserAdvice() {
System.out.println("After getting a user...");
}
- After Throwing Advice:
@AfterThrowing(value="execution(* com.example.UserService.deleteUser(..))", throwing="ex")
public void afterDeleteUserAdvice(Throwable ex) {
System.out.println("Exception during delete: " + ex.getMessage());
}
- After Advice (Finally):
@After("execution(* com.example.UserService.*(..))")
public void afterUserServiceAdvice() {
System.out.println("After UserService method call...");
}
- Around Advice:
@Around("execution(* com.example.UserService.getAllUsers(..))")
public Object aroundGetAllUsersAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Before getting all users...");
Object result = joinPoint.proceed(); // Proceed with the original method
System.out.println("After getting all users...");
return result; // Return the result
}
Integrating AOP with Logging
Without IoC
public class SimpleSpellChecker implements SpellChecker {
public void checkSpelling() {
System.out.println("Inside checkSpelling method.");
Logger.getLogger(SimpleSpellChecker.class.getName()).info("Starting checkSpelling...");
}
}
public class TextEditor {
private SpellChecker spellChecker;
public TextEditor() {
spellChecker = new SimpleSpellChecker(); // Manual instantiation
}
public void spellCheck() {
spellChecker.checkSpelling();
Logger.getLogger(TextEditor.class.getName()).info("Finished spellCheck...");
}
}
With IoC
@Component
public class SimpleSpellChecker implements SpellChecker {
@Override
public void checkSpelling() {
System.out.println("Inside checkSpelling method.");
}
}
@Component
public class TextEditor {
private SpellChecker spellChecker;
@Autowired // Spring automatically injects the dependency
public TextEditor(SpellChecker spellChecker) {
this.spellChecker = spellChecker;
}
public void spellCheck() {
spellChecker.checkSpelling();
}
}
@Aspect
@Component
public class LoggingAspect {
@Before("execution(* com.example.*.*(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Starting method execution: " + joinPoint.getSignature().getName());
}
@After("execution(* com.example.*.*(..))")
public void logAfter(JoinPoint joinPoint) {
System.out.println("Finishing method execution: " + joinPoint.getSignature().getName());
}
}
Conclusion
Using AOP in Spring allows for modular and maintainable code by separating cross-cutting concerns like logging. This makes it easier to manage and apply behaviors across various components without changing their source code.
Other AOP Features
1- Caching
Use AOP to cache the results of expensive method calls.
@Aspect
@Component
public class CachingAspect {
private Map<String, Object> cache = new HashMap<>();
@Around("execution(* com.example.service.*.*(..))")
public Object cacheResult(ProceedingJoinPoint joinPoint) throws Throwable {
String key = joinPoint.getSignature().toString();
if (cache.containsKey(key)) {
return cache.get(key); // Return cached result
}
Object result = joinPoint.proceed(); // Proceed with method execution
cache.put(key, result); // Cache the result
return result;
}
}
2- Transactions
Manage transactions without cluttering business logic.
@Aspect
@Component
public class TransactionAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Transaction started.");
try {
Object result = joinPoint.proceed(); // Proceed with method execution
System.out.println("Transaction committed.");
return result;
} catch (Exception e) {
System.out.println("Transaction rolled back.");
throw e; // Handle exception
}
}
}
3- Exception Handling
Centralize exception handling logic.
@Aspect
@Component
public class ExceptionHandlingAspect {
@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
public void handleException(Throwable ex) {
System.out.println("Exception occurred: " + ex.getMessage());
// Additional logging or handling logic here
}
}
4- Performance Monitoring
Measure method execution times.
@Aspect
@Component
public class PerformanceMonitoringAspect {
@Around("execution(* com.example.service.*.*(..))")
public Object monitorPerformance(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
Object result = joinPoint.proceed(); // Proceed with method execution
long duration = System.currentTimeMillis() - start;
System.out.println("Execution time: " + duration + " ms");
return result;
}
}
5- Security
Apply security checks across methods.
@Aspect
@Component
public class SecurityAspect {
@Before("execution(* com.example.service.*.*(..))")
public void checkSecurity() {
// Implement security checks here
System.out.println("Security check passed.");
}
}
6- Validation
Ensure input data validity.
@Aspect
@Component
public class ValidationAspect {
@Before("execution(* com.example.service.*.*(..)) && args(input)")
public void validateInput(String input) {
if (input == null || input.isEmpty()) {
throw new IllegalArgumentException("Input cannot be null or empty.");
}
}
}
These code snippets demonstrate how to implement common AOP features using Spring, promoting better code organization and separation of concerns.