:Aspect Oriented Programming
/공통 관심 사항과 핵심 관심 사항을 분리
시간 측정 로직을 TimeTraceAop라는 곳에 모아놓고,
helloController든, memberService든, memberRepository든
내가 원하는 곳에 적용해줌.
우선 hello - helloSpring에 aop라는 패키지를 하나 생성하였다.
그리고 그 밑에 TimeTraceAop라는 클래스를 생성하였다.
AOP는 @Aspect라는 애노테이션을 필요로 하기 때문에 적어주었고,
public Object excute(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 없이 했던 코드와 비슷하게 진행된다.
return joinPoint.proceed(); 는
Object result = joinPoint.proceed()
return result
를 Inline으로 Refactor 해준 것이다.
joinPoint.proceed()는
다음 메소드로 진행하게 해준다.
이제 등록을 하기 위해서는
TimeTraceAop에 @Component 라는 애노테이션을 적는 방법과,
더 좋은 방법은
SpringConfig의 springbean에 등록을 하는 것이다.
@Bean
public TimeTraceAop timeTraceAop() {
return new TimeTraceAop();
}
이렇게 스프링 빈에 직접 등록을 하는 방법이
확실하게 Aop가 등록돼서 쓰이는 구나 를 알 수 있기 때문에 더 좋은 방법이다.
여기서는 그냥 @Component를 사용하겠다.
그리고 이제 이 공통 관심 사항을 '어디에' 적용할 것인지를 targeting 해주기 위해
@Around("execution(* hello.hellospring..*(..))")
를 적어준다.
hello.hellospring의 모든 하위 패키지에 적용한다는 뜻이다.
여기에는 클래스명도 적을 수 있고 여러가지 다양한 것들을 적을 수가 있다.
예를 들어 service와 service 하위만 측정하고 싶으면,
@Around("execution( hello.hellospring.service..(..))")
이렇게 적어주면 된다.
이렇게 코드를 작성한 후,
실행을 시켜보았다.
HelloSpringApplication을 돌려서
서버를 띄우고,
회원 목록에 들어가본 후,
실행 결과를 보니
이렇게 MemberController, MemberService, JpaRepository들이 딱딱 나오고,
END: execution(List org.springframework.data.jpa.repository.JpaRepository.findAll()) 97ms
DB에서 조회하는 것 - 97ms
END: execution(List hello.hellospring.service.MemberService.findMembers()) 105ms
MemberService - 105ms
END: execution(String hello.hellospring.controller.MemberController.list(Model)) 121ms
MemberController - 121ms
이렇게 모두 나오는 것을 볼 수 있다.
이렇게 하면 어디에서 병목현상이 있는지 찾을 수가 있다.
호출이 될 때마다 joinPoint.로 원하는 것을 조작할 수 있다.
joinPoint가 다음으로 호출을 해주면서, intercept 가 걸리는 것이다.
이런 식으로 intercepting해서 풀어나갈 수 있는 이러한 기술이 바로 AOP이다.
이렇게 해서,
AOP를 사용하지 않고 시간을 측정했을 때 발생했던 문제들을
해결할 수 있는 것이다.
AOP를 적용하기 전의 의존관계는,
helloController가 memberService를 의존하고 있고,
의존 관계에 의해 호출을 했을 것이다.
그런데 AOP에서는
적용할 위치를 지정을 하면,
프록시라고 하는 가짜 memberService를 만들어 내고,
컨테이너에 스프링 빈을 등록할 때,
진짜 스프링 빈 말고
가짜 스프링 빈 앞에 세워놓는다.
그리고 가짜 스프링 빈이 끝나면(joinPoint.proceed())
그때 진짜를 호출해 준다.
그래서
helloController가 호출하는 것은
진짜 memberService가 아닌
프록시라는 가짜 memberService인 것이다.
controller - MemberController에
@Autowired
public MemberController(MemberService memberService) {
this.memberService = memberService;
System.out.println("memberService = " + memberService.getClass());
}
이렇게 코드를 추가해 주었다.
결과는 가짜 memberService가 호출된 것을 확인해 볼 수 있었다.
<AOP 적용 전 전체 그림>
<AOP 적용 후 전체 그림>