AspectJ is an independent AOP framework rather than a native Spring module. It is commonly integrated alongside Spring's own AOP capabilities to provide a more robust aspect-oriented solution.
Two primary approaches exist for this integration:
- Annotation-driven confgiuration (Recommended)
- XML-based configuration (Supplementary)
Dependency Configuration
Include the required libraries in your project setup. The spring-aspects module inherently bundles the AspectJ weaver, making an explicit aspectjweaver dependency unnecessary.
<dependencies>
<!-- Spring Core -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.5</version>
</dependency>
<!-- Spring AOP + AspectJ Integration -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.3.5</version>
</dependency>
<!-- AOP Alliance API -->
<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>
<!-- Logging -->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
<!-- Database Connection Pool -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.10</version>
</dependency>
<!-- Database Driver -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.22</version>
</dependency>
<!-- Unit Testing -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
<scope>test</scope>
</dependency>
<!-- Code Generation -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>provided</scope>
</dependency>
</dependencies>
Pointcut Expressions
Pointcut expressions define the target methods for AOP enhancement using the execution pattern.
Syntax: execution([modifier] [returnType] [fullClassPath] [methodName]([params]))
Examples targeting different scopes:
execution(* com.techcorp.repository.AccountRepoImpl.create(..))- Targets a single specific methodexecution(* com.techcorp.repository.AccountRepoImpl.*(..))- Targets all methods within a single classexecution(* com.techcorp.repository.*.*(..))- Targets all methods across all classes in the repository packageexecution(* com.techcorp.repository.*.create(..))- Targets allcreatemethods across classes in the packageexecution(* com.techcorp.repository.*.create*(..))- Targets all methods starting withcreatein the package
XML Configuration Approach
Enable component scanning and automatic proxy generation through XML configuration.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.techcorp"/>
<aop:aspectj-autoproxy/>
</beans>
Target Interfaces and Implementations
Define the DAO interfaces.
package com.techcorp.repository;
public interface AccountRepo {
int createAccount(Integer accountId, String accountName);
}
package com.techcorp.repository;
public interface EmployeeRepo {
int registerEmployee(Integer empId, String empName, String role);
}
Implement the interfaces and annotate them as Spring beans.
package com.techcorp.repository.impl;
import com.techcorp.repository.AccountRepo;
import org.springframework.stereotype.Repository;
@Repository
public class AccountRepoImpl implements AccountRepo {
@Override
public int createAccount(Integer accountId, String accountName) {
System.out.println("Account creation logic executed...");
return 1;
}
}
package com.techcorp.repository.impl;
import com.techcorp.repository.EmployeeRepo;
import org.springframework.stereotype.Repository;
@Repository
public class EmployeeRepoImpl implements EmployeeRepo {
@Override
public int registerEmployee(Integer empId, String empName, String role) {
System.out.println("Employee registration logic executed...");
return 1;
}
}
Aspect Definition
Create the aspect class containing the advice logic. The @Aspect annotation marks this class as an aspect, while @Component registers it as a Spring bean.
package com.techcorp.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class RepositoryAspect {
// Shared pointcut definition targeting all creation methods
@Pointcut("execution(* com.techcorp.repository.*.create*(..))")
public void creationPointcut() {}
// Before advice: Executes prior to the target method
// JoinPoint provides access to method signature and arguments
@Before("creationPointcut()")
public void logBeforeExecution(JoinPoint jp) {
System.out.println("Pre-processing: Method " + jp.getSignature().getName() + " is about to run.");
}
// After advice: Executes regardless of whether the target method succeeds or throws an exception
@After("creationPointcut()")
public void logAfterExecution(JoinPoint jp) {
System.out.println("Post-processing: Method " + jp.getSignature().getName() + " has finished.");
}
// AfterReturning advice: Executes only if the target method completes successfully without exceptions
// The 'returning' attribute binds the method result to the advice parameter
@AfterReturning(value = "creationPointcut()", returning = "result")
public void logSuccessfulReturn(JoinPoint jp, Object result) {
System.out.println("Successful return detected. Output: " + result);
}
// AfterThrowing advice: Executes only when the target method throws an exception
// The 'throwing' attribute binds the exception object to the advice parameter
@AfterThrowing(value = "creationPointcut()", throwing = "err")
public void logExceptionOccurrence(Throwable err) {
System.out.println("Exception caught in aspect: " + err.getMessage());
}
// Around advice: Wraps the target method, providing control over execution timing
// ProceedingJoinPoint.proceed() triggers the actual target method
// The result of proceed() must be returned to preserve the original method's return value
@Around("creationPointcut()")
public Object monitorExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Around phase A: Entering method monitor.");
Object output = pjp.proceed();
System.out.println("Around phase B: Exiting method monitor.");
return output;
}
}
Testing the XML Configuration
@Test
public void verifyXmlSetup() {
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountRepo accountRepo = ctx.getBean(AccountRepo.class);
accountRepo.createAccount(101, "AdminUser");
}
Managing Multiple Aspects
When multiple aspects target the same method, the @Order annotation dictates the execution sequence. A lower numeric value signifies higher priority, placing the aspect closer to the target method invocation point.
Pure Annotation Configuration Approach
Eliminate XML entirely by utilizing a Java-based configuration class.
package com.techcorp.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = "com.techcorp")
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AppConfig {
}
Testing the Java Configuration
@Test
public void verifyJavaSetup() {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
AccountRepo accountRepo = ctx.getBean(AccountRepo.class);
accountRepo.createAccount(101, "AdminUser");
}