만약 모든 메서드의 호출 시간을 측정해야 한다면..
모든 메서드에가서 시간 호출 코드를 추가해줘야 한다. 근데 이게 단순히 코드를 추가하는게 아니라 핵심 로직 코드를 건드리면서 추가해줘야해
// 회원가입
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");
}
}
// 전체 회원 조회
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");
}
}
이런식으로 하나하나 바꿔줘야 되는데 두 개 했는데도 귀찮다..
그리고 여러가지 문제점이 생긴다
문제
- 시간을 측정하는 기능은 핵심 관심 사항이 아닌 공통 관심 사항이다
- 시간을 측정하는 로직과 핵심 비즈니스의 로직이 섞이면 유지보수가 어려워진다
- 시간을 측정하는 로직을 별도의 공통 로직으로 만들기 매우 어렵다
- 시간을 측정하는 로직을 변경하려면 모든 로직을 찾아가 하나하나 변경해줘야 한다 = 귀찮다
AOP는 Aspect Oriented Programming의 약자로 공통 관심 사항(시간 측정)과 핵심 관심 사항(회원가입, 회원목록)을 분리 시켜준다.
이런식으로!!
aop/TimeTraceAop을 생성하고 다음과 같은 코드를 작성해주면 끝!
@Component // 스프링 빈 등록. SpringConfig 에서 등록 해주는게 더 좋다
@Aspect // aop 쓰려면 써야함
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");
}
}
}
이렇게 작성후 서버 키면
이런식으로 시간이 측정된다!!!
해결
- 공통 관심 사항(시간 측정)과 핵심 관심 사항(회원가입, 회원목록)을 분리
- 시간을 측정하는 로직을 별도의 공통 로직으로 만듦
- 변경이 필요하면 이 로직만 변경하면 된다
- 원하는 적용 대상을 선택할 수 있다(@Around())
- 핵심 관심 사항을 깔끔하게 유지
실행시 프록시
라고 불리는 가짜 스프링 빈을 띄워서 동작 시킨 후 진짜 스프링 빈을 띄워서 실행한다나 뭐라나..