AOP가 필요한 상황
- 모든 메서드의 호출 시간을 측정하고 싶을때
- 공통 관심 사항 (cross-cutting concern) vs 핵심 관심 사항(core concern)
- 회원 가입 시간, 회원 조회 시간을 측정하고 싶을때
MemberService 회원 조회 시간 측정 추가
package hello.hellospring.service;
import hello.hellospring.domain.Member;
import hello.hellospring.repository.MemberRepository;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
@Transactional
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
//회원가입
public Long join(Member member) {
long start = System.currentTimeMillis();
try{
validateDuplicateMember(member); //중복 회원 검증
memberRepository.save(member);
return member.getId();
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("join " + timeMs + "ms");
}
}
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m -> {
throw new IllegalStateException("이미 존재하는 회원입니다.");
});
}
//전체 회원 조회
public List<Member> findMembers() {
long start = System.currentTimeMillis();
try {
return memberRepository.findAll();
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("findMembers " + timeMs + "ms");
}
}
public Optional<Member> findOne(Long memberId) {
return memberRepository.findById(memberId);
}
}
이런식으로 메서드 하나하나 시간 찍어보는것에는 많은 문제가 따른다
- 회원가입, 회원 조회에 시간을 측정하는 기능은 핵심 관심 사항이 아니다
- 시간을 측정하는 로직은 공통 관심 사항이다
- 시간을 측정하는 로직과 핵심 비즈니스의 로직이 섞여서 유지보수가 어렵다
- 시간을 측정하는 로직을 별도의 공통 로직으로 만들기 매우 어렵다
- 시간을 측정하는 로직을 변경할 때 모든 로직을 찾아가면서 변경해야 한다
AOP 적용
AOP : Aspect Oriented Programming
공통 관심 사항(cross-cutting concern)과 핵심 관심 사항(core concern) 분리한다
시간 측정 AOP 등록
package hello.hellospring.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class TimeTraceAop {
@Around("execution(* hello.hellospring..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis();
System.out.println("START: " + joinPoint.toString());
try {
return joinPoint.proceed();
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("END: " + joinPoint.toString()+ " " + timeMs + "ms");
}
}
}
이렇게 하면 위에 문제를 해결 할 수 있다
- 회원가입, 회원 조회등 핵심 관심사항과 시간을 측정하는 공통 관심 사항을 분리한다
- 시간을 측정하는 로직을 별도의 공통 로직으로 만들었다
- 핵심 관심 사항을 깔끔하게 유지할 수 있다
- 변경이 필요하면 이 로직만 변경하면 된다
- 원하는 적용 대상을 선택할 수 있다
AOP 동작 원리
AOP 적용 전에는 memberController 에서 memberService를 바로 호출한다
AOP 적용 후에는 memberController 에서 호출할때
중간에 가짜 memberService를 거친다 프록시라고 하는데
프록시에서 처리할꺼 처리하고 joinPoint.proceed() 해서
실제 memberService를 호출하게 된다
'Spring' 카테고리의 다른 글
[Spring] 스프링의 핵심은 무엇인가요? (스프링부트 개념정리 / 인프런 최주호님 강의) (0) | 2022.10.06 |
---|---|
[Spring] MVC 패턴, Controller/Service/Repository (0) | 2022.10.02 |
[Spring] 스프링 DB 접근 기술 (스프링입문 / 인프런 김영한님 강의) (0) | 2022.10.01 |
[Spring] 회원관리예제 (스프링입문 / 인프런 김영한님 강의) (0) | 2022.10.01 |
[Spring] 스프링 빈과 의존관계 (스프링입문 / 인프런 김영한님 강의) (0) | 2022.10.01 |