AspectJ is a powerful AOP (Aspect-Oriented Programming) framework that defines its own syntax for cross-cutting concerns and uses a bytecode weaver to generate standard Java class files. Spring integrates AspectJ’s capabilities to simplify AOP implementation.
AspectJ supports several types of advice:
- Before advice: Executes before the target method.
- After returning advice: Runs after a successful method return.
- Around advice: Wraps the target method, allowing custom behavior before and after execution.
- After throwing advice: Triggers when the target method throws a exception.
- After (finally) advice: Always executes, regardless of outcome—similar to a
finallyblock.
Pointcut Expressions
Pointcuts determine which methods are intercepted by aspects. The genarel syntax is:
execution(modifiers? returnType declaringType? methodName(paramList) throws?)
Key wildcards:
*matches zero or more characters in names or types...in parameters means any number of arguments; in package names, it includes subpackages.+after a type matches the class/interface and all subclasses/implementations.
Examples:
execution(public * *(..))→ any public method.execution(* set*(..))→ any method starting with "set".execution(* com.example.service..*.*(..))→ all methods inserviceand subpackages.execution(* add(String, int))→addmethod with specific parameter types.
XML-Based Configuration
- Target service:
public class StudentService implements IStudentService {
public boolean addStudent(Student student) {
System.out.println("Executing add operation");
return true;
}
public boolean delStudent(Integer id) {
int x = 1 / 0; // intentional exception
System.out.println("Executing delete");
return true;
}
// other methods...
}
- Aspect class:
public class LoggingAspect {
public void logBefore() {
System.out.println("[Before] Preparing to execute method");
}
public void logAfterReturning(Object result) {
System.out.println("[AfterReturning] Result: " + result);
}
public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("[Around] Before method");
Object res = joinPoint.proceed();
System.out.println("[Around] After method");
}
public void logOnException(Exception ex) {
System.out.println("[AfterThrowing] Exception: " + ex.getMessage());
}
public void logFinally() {
System.out.println("[After] Cleanup executed");
}
}
- Spring XML configurasion (
applicationContext.xml):
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
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/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="studentService" class="com.example.service.impl.StudentService" />
<bean id="loggingAspect" class="com.example.aspect.LoggingAspect" />
<aop:config>
<aop:pointcut id="addMethods" expression="execution(* add*(..))" />
<aop:pointcut id="updateMethods" expression="execution(* update*(..))" />
<aop:pointcut id="queryMethods" expression="execution(* get*(..))" />
<aop:pointcut id="deleteMethods" expression="execution(* del*(..))" />
<aop:aspect ref="loggingAspect">
<aop:before method="logBefore" pointcut-ref="addMethods" />
<aop:after-returning method="logAfterReturning(java.lang.Object)"
pointcut-ref="updateMethods" returning="result" />
<aop:around method="logAround" pointcut-ref="queryMethods" />
<aop:after-throwing method="logOnException(java.lang.Exception)"
pointcut-ref="deleteMethods" throwing="ex" />
<aop:after method="logFinally" pointcut-ref="deleteMethods" />
</aop:aspect>
</aop:config>
</beans>
Annotation-Based Configuration
Using annotations reduces XML but introduces code-level coupling.
- Annotated aspect:
@Aspect
public class AnnotationLoggingAspect {
@Before("execution(* add*(..))")
public void logBefore() {
System.out.println("[Before] via annotation");
}
@AfterReturning(pointcut = "execution(* update*(..))", returning = "result")
public void logAfterReturning(Object result) {
System.out.println("[AfterReturning] Result: " + result);
}
@Around("execution(* get*(..))")
public Object logAround(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("[Around] Start");
Object output = pjp.proceed();
System.out.println("[Around] End");
return output;
}
@AfterThrowing(pointcut = "execution(* del*(..))", throwing = "ex")
public void logOnException(Exception ex) {
System.out.println("[AfterThrowing] Error: " + ex);
}
@After("execution(* del*(..))")
public void logFinally() {
System.out.println("[After] Final cleanup");
}
}
- Minimal XML setup:
<beans ...>
<bean id="studentService" class="com.example.service.impl.StudentService" />
<bean id="annotationAspect" class="com.example.aspect.AnnotationLoggingAspect" />
<aop:aspectj-autoproxy />
</beans>
Both approaches require the aspectjweaver library on the classpath. The annotation style is more concise but embeds AOP logic directly into Java classes, whereas XML keeps concerns externalized.