Gymterview
middle

Какие типы Advice существуют в Spring AOP?

В Spring AOP существует пять типов advice, определяющих, когда выполняется дополнительная логика относительно целевого метода.

Тип Когда выполняется
@Before До вызова метода
@AfterReturning После успешного возврата метода
@AfterThrowing После выброса исключения
@After Всегда после метода (аналог finally)
@Around Оборачивает вызов целиком (самый мощный)

Порядок выполнения

@Around (начало) -> @Before -> метод -> @AfterReturning / @AfterThrowing -> @After -> @Around (конец)

Примеры всех типов advice
@Before("execution(* com.example.service.*.*(..))")
public void beforeAdvice(JoinPoint joinPoint) {
    System.out.println("До вызова: " + joinPoint.getSignature().getName());
}

@AfterReturning(pointcut = "execution(* com.example.service.*.*(..))", returning = "result")
public void afterReturningAdvice(JoinPoint joinPoint, Object result) {
    System.out.println("Метод вернул: " + result);
}

@AfterThrowing(pointcut = "execution(* com.example.service.*.*(..))", throwing = "ex")
public void afterThrowingAdvice(JoinPoint joinPoint, Exception ex) {
    System.out.println("Исключение: " + ex.getMessage());
}

@After("execution(* com.example.service.*.*(..))")
public void afterAdvice(JoinPoint joinPoint) {
    System.out.println("Метод завершён (в любом случае)");
}

@Around("execution(* com.example.service.*.*(..))")
public Object aroundAdvice(ProceedingJoinPoint pjp) throws Throwable {
    long start = System.currentTimeMillis();
    try {
        Object result = pjp.proceed(); // вызов оригинального метода
        return result;
    } finally {
        long elapsed = System.currentTimeMillis() - start;
        System.out.println(pjp.getSignature().getName() + " выполнен за " + elapsed + " мс");
    }
}

Практический пример – замер времени через собственную аннотацию

Пример
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Timed { }

@Aspect
@Component
public class TimingAspect {
    @Around("@annotation(Timed)")
    public Object measureTime(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.nanoTime();
        Object result = pjp.proceed();
        long duration = (System.nanoTime() - start) / 1_000_000;
        log.info("{}.{} выполнен за {} мс",
                pjp.getTarget().getClass().getSimpleName(),
                pjp.getSignature().getName(), duration);
        return result;
    }
}

На собеседовании: важно знать все 5 типов и порядок их выполнения. Частая ошибка в @Around – забыть вызвать pjp.proceed() (метод не выполнится) или забыть вернуть результат proceed() (метод вернёт null).