Аспектно-ориентированное программирование (АОП) - это парадигма программирования, являющаяся дальнейшим развитием процедурного программирования и ООП. Идея АОП заключается в выделении так называемой сквозной функциональности
В Spring AOP существуют следующие понятия:
Join Point - точки соединения, в которых внедряется сквозная функциональность
Pointcut - срез, выражение, определяющее набор точек соединения, к которым применяется функциональность
Advice - действие, выполняемый в определенный момент выполнения метода:
@Before
- перед вызовом метода@After
или @AfterFinally
- после вызова метода@AfterReturning
- если метод что-то вернул@AfterThrowing
- если метод выбросил исключение@Around
- параллельно с работой методом (например, чтобы засечь время работы метода)Далее они собираются в класс с аннотацией @Aspect
. Пример: имеется метод, который нужно логировать (так называемый Target). В этом случае, создаем интерфейс:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public interface Loggable {
}
@Aspect
@Component
public class LoggingAspect {
@Before("@annotation(Loggable)")
public void logBefore(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("➡ Вызов метода: " + methodName);
}
@AfterReturning(pointcut = "@annotation(Loggable)", returning = "result")
public void logAfter(JoinPoint joinPoint, Object result) {
String methodName = joinPoint.getSignature().getName();
System.out.println("✅ Метод " + methodName + " завершился. Результат: " + result);
}
@AfterThrowing(pointcut = "@annotation(Loggable)", throwing = "ex")
public void logException(JoinPoint joinPoint, Throwable ex) {
String methodName = joinPoint.getSignature().getName();
System.out.println("❌ Метод " + methodName + " выбросил исключение: " + ex.getMessage());
}
}
Здесь в качестве pointcut мы отмечаем все методы, которые были аннотированы @Loggable
. Чтобы не хардкодить строки, можно создать отдельный метод для pointcut:
@Aspect
@Component
public class LoggingAspect {
@Pointcut("@annotation(Loggable)")
public void loggableMethods() { }
@Before("loggableMethods()")
public void logBefore(JoinPoint joinPoint) {
String methodName = joinPoint.getSignature().getName();
System.out.println("➡ Вызов метода: " + methodName);
}
...
}
Тогда на сам метод достаточно повесить аннотацию интерфейса:
@Service
public class UserService {
@Loggable
public String getUserById(Long id) {
if (id == null) {
throw new IllegalArgumentException("ID не может быть null");
}
return "Пользователь #" + id;
}
}
Также можно обозначить в pointcut все вызовы методов из определенного пакета: @Pointcut("execution(* com.example..*(..))")
АОП работает преимущественно в рантайме, однако расширение AspectJ может скомпилировать бины с аспектами заранее (так называемый Weaving). AspectJ расширяет синтаксис Java:
aspect LoggingAspect {
pointcut logMethods(): execution(* com.example..*(..));
before(): logMethods() {
System.out.println("Вызов метода: " + thisJoinPoint.getSignature());
}
}
Если целевой объект реализует какой-нибудь интерфейс, то Spring AOP будет использовать динамический прокси JDK, позволяющий проксировать интерфейс. Иначе будет использоваться CGLIB-прокси
Преимущества AOP:
Потенциальные недостатки: