"공통(횡단) 관심사를 모듈화하여 핵심 비즈니스 로직과 분리시키는 것"
OOP가 객체 단위로 코드를 구성한다면, AOP는 관심사 단위로 코드를 구성

실행흐름 예시
1. 클라이언트가 userServiceImpl.registerUser() 호출
2. Spring이 userServiceImpl의 프록시 객체를 먼저 실행
3. 프록시 객체에서 @Before Aspect를 먼저 실행
4. Aspect가 끝난 후 userServiceImpl.registerUser() 호출
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ServiceCache {
}
@Aspect
@Component
public class ServiceCacheAspect {
private final Map<String, Object> cache = new ConcurrentHashMap<>();
@Around("@annotation(com.sprint.mission.springdemo.pr1.cache.ServiceCache)") // ServiceCache라는 어노테이션이 달려있는 메소드에만 Aspect 적용
public Object cacheResult(ProceedingJoinPoint joinPoint) throws Throwable {
String cacheKey = generateKey(joinPoint);
if (cache.containsKey(cacheKey)) {
return cache.get(cacheKey);
}
Object result = joinPoint.proceed();
cache.put(cacheKey, result);
return result;
}
// 캐시의 키값을 직접 생성 - 같은 메소드가 같은 인자를 가지면 같은 키값을 가지도록!
private String generateKey(ProceedingJoinPoint joinPoint) {
StringBuilder key = new StringBuilder();
key.append(joinPoint.getSignature().toShortString()); // 메소드 이름을 받아와서 문자열로 바꾸기
for (Object arg : joinPoint.getArgs()) {
key.append("-").append(arg.toString()); // 호출하는 인자들도 하나씩 다 붙여줌
}
return key.toString();
}
}
@Service
public class UserServiceImpl implements UserService {
@Override
@ServiceCache
public User getUser(UUID userId) {
return userRepository.findById(userId);
}
}
@Transactional // Spring에서 제공하는 AOP 어노테이션!
public void register(String username) {
userRepository.save(new User(username));
// 일부러 예외 발생시켜보기 (롤백 테스트)
if (username.equals("error")) {
throw new RuntimeException("등록 중 에러 발생!");
}
pointRepository.save(new Point(username, 100));
}
@Service
public class ProductService {
// 실제로는 JPA repository에서 가져온다고 가정
@Cacheable("products") // 캐시 이름: "products"
public Product getProductById(Long productId) {
System.out.println("DB 호출: getProductById " + productId);
return new Product(productId, "상품 " + productId);
}
}
=> 해당 어노테이션들을 붙이면, 프록시 객체를 만들고 내부 로직에 따라 해당 로직(Advice)을 수행한 후, 원본 객체의 메서드 실행!
참고 - https://velog.io/@kai6666/Spring-Spring-AOP-%EA%B0%9C%EB%85%90