이전에 다이나믹 프록시로 작성했던 예제를 Spring AOP, 어노테이션을 사용해서 개선해보자.
UserService.java
public interface UserService {
void doAction();
}
UserServiceImpl.java
import org.springframework.stereotype.Service;
@Service
public class UserServiceImpl implements UserService {
@Override
@TestTransactional
public void doAction() {
System.out.println("doAction!");
}
}
TestTransactional.java(Annotation)
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface TestTransactional {
}
Spring의 @Transactional 처럼 사용할 테스트용 커스텀 어노테이션인 @TestTransactional을 생성한다.
TransactionAspect.java
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class TransactionAspect {
@Around("@annotation(TestTransactional)")
public Object manageTransaction(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Transaction start ");
try {
Object result = joinPoint.proceed();
System.out.println("Transaction commit for " + joinPoint.getSignature());
return result;
} catch (Exception e) {
System.out.println("Transaction rollback for " + joinPoint.getSignature());
throw e;
}
}
}
@Aspect 어노테이션을 추가하여 AOP 담당 객체임을 지정한다.
@Around 안에 Pointcut 표현식은 TestTransactional 어노테이션이 적용된 모든 메서드가 실행될 때 이 어드바이스를 적용한다라는 의미이다.
@Around 사용 시 ProceedingJoinPoint 객체가 매개변수로 제공되며, proceed() 메서드를 호출함으로써 대상 메서드를 실행하고 결과를 반환받을 수 있다. 이 메서드 호출 전후로 필요한 로직을 추가하여 메서드의 실행 과정에 개입할 수 있다.
위 예제에서는 proceed() 메서드 호출 전 트랜잭션 시작, 호출 후에 commit을 수행하며 예외 발생 시 rollback을 수행하도록 작성된 것을 확인할 수 있다.