[25강]
2023. 2. 6. 14:56ㆍSpring 강의/section7
AOP 적용
- AOP : Aspect Oriented Programming - 관점 지향 프로그래밍
- 공통 관심 사항(cross-cutting concern) vs 핵심 관심 사항(core concern) 분리
이전처럼 모든 메소드마다 시간 측정 로직을 붙이는 게 아니라
원하는 메소드에 시간 측정 로직을 적용시킬 수 있다.
먼저 hello - hellospring2 밑에 aop란 패키지를 만들고 그 안에 TimeTraceAop 클래스를 만든다.
TimeTraceAop.class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
package hello.hellospring2.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect //붙여줘야 AOP로 쓸 수 있다.
@Component
public class TimeTraceAop {
@Around("execution(* hello.hellospring2..*(..))")
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");
}
}
}
|
cs |
@Aspect : 붙어줘야 AOP로 쓸 수 있다.
@Component : 빈으로 등록시켜준다.
@Around : 해당 로직을 어떤 클래스에 사용할 것인지 정할 수 있다.
@Around("execution(* hello.hellospring2..*(..))") : hello.hellospring2 밑에 있는 모든 클래스에 적용시키겠다.
이제 MemberService 클래스는 핵심 로직만으로 깔끔하게 유지할 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
|
package hello.hellospring2.service;
import hello.hellospring2.domain.Member;
import hello.hellospring2.repository.MemberRepository;
import hello.hellospring2.repository.MemoryMemberRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.Optional;
@Transactional //jpa를 쓰려면 항상 있어야 한다//data를 저장하고 변경할 때 필요
public class MemberService {
private final MemberRepository memberRepository;
public MemberService(MemberRepository memberRepository) {
this.memberRepository = memberRepository;
}
/* 회원 가입 */
public Long join (Member member){
validateDuplicateMember(member); //중복회원 검증
memberRepository.save(member);
return member.getId();
}
private void validateDuplicateMember(Member member) {
memberRepository.findByName(member.getName())
.ifPresent(m -> {
throw new IllegalStateException("이미 존재하는 회원입니다.");
});
}
/*전체 회원 조회 */
public List<Member> findMembers(){
return memberRepository.findAll();
};
public Optional<Member> findOne(Long memberId){
return memberRepository.findById(memberId);
}
}
|
cs |
실행 결과
처음 서버를 실행했을 때
회원을 등록할 때
회원을 조회할 때
모든 시간을 알 수 있다.
해결
- 회원가입, 회원 조회 등 핵심 관심사항과 시간을 측정하는 공통 관심 사항을 분리한다.
- 시간을 측정하는 로직을 별도의 공통 로직으로 만들었다.
- 핵심 관심 사항을 깔끔하게 유지할 수 있다.
- 변경이 필요하다면 이 로직만 변경하면 된다.
- 원하는 적용 대상을 선택할 수 있다.
스프링의 AOP 동작 방식 설명
AOP 적용 전 의존관계
바로 memberController에서 memberService를 호출한다.
AOP 적용후 의존관계
aop가 있으면 가짜 memberSerivce를 만들어낸다.
스프링 컨테이너에 스프링 빈을 등록할 때 가짜 스프링 빈을 앞에 세워둔다.
그리고 가짜 스프링 빈이 joinPoint.proceed()하면 그때 진짜 memberSerivce를 호출한다.
memberController가 호출하는 것은 프록시라는 기술로 발생하는 가짜 memberService이다.
AOP 적용 전 전체 그림
AOP 적용 후 전체 그림
컨테이너에서 스프링 빈을 관리하면 가짜를 만들어서 가짜를 진짜에 DI해주면 된다.
이게 바로 DI의 장점!