[Spring] AOP

woozxn·2021년 7월 14일

Spring

목록 보기
8/8
post-thumbnail

🌕 AOP가 필요한 상황

핵심 관심사항(core concern)
회원 가입, 회원 조회와 같은 비즈니스 로직 같은 경우
공통 관심사항(cross-cutting concern)
회원 가입, 회원 조회의 기능을 실행하는데 걸리는 시간을 측정하는 것 같은 기능

🌒 회원 가입과 회원 조회의 기능의 호출 시간을 구하기

메서드 시작 전 start로 시간을 받고, finally에서 메서드가 끝난 시점 시간을 재 두 시간의 차로 호출에 걸리는 시간 측정.

src\main\java\hello\hellospring\service\MemberService.java에 다음 코드 추가

@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");
        }
    }

    /**
     * 전체 회원 조회
     */
    public List<Member> findMembers(){
        
        long start = System.currentTimeMillis();
        
        try {
            return memberRepository.findAll();
        } finally {
            long finish = System.currentTimeMillis();
            long timeMs = finish - start;
            System.out.println(timeMs);
        }
    }
}

이러한 방법으로 메서드에 코드를 추가하여 호출 시간을 구하게 되면 핵심 관심사항과 공통 관심사항이 섞여 유지 보수가 어려워지는 단점이 생기게됩니다.

따라서 이런 상황에서 AOP가 필요!

🌕 AOP 적용

AOP(Aspect Oriented Programming)

공통 관심 사항(cross-cutting concern)핵심 관심 사항(core concern) 분리하여 개발하는 기술

🌒 시간 측정 AOP 등록

src\main\java\hello\hellospring\aop\TimeTraceAop.java 생성

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;

@Aspect
@Component
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 애노테이션을 사용해 우리가 시간 측정 로직을 어떤 부분에 적용하고 싶은지 명시하고, 회원 가입, 회원 목록 조회 등의 메서드가 호출될 때마다 ProceedingJoinPoint 객체인 joinPoint가 넘어오면서 execute() 메서드가 호출됩니다.
메서드가 호출되면 시작 시간을 재고, 출력 후 try문 안의 joinPoint.proceed()가 호출될 때 우리가 실행하기 원하는 로직(회원 가입, 회원 조회)을 실행합니다.
메서드가 종료되면 finally에서 종료 시간을 재고, 시간을 측정해 출력한뒤 메서드 종료.

@Aspect 애노테이션을 사용하면 이 메서드가 AOP 기능을 할 수 있게합니다.
AOP 또한 스프링 빈으로 등록되어야 하기 때문에 컴포넌트 스캔을 위해 @Component로 스프링 빈을 등록합니다.
(Config파일의 확인을 통해 어떤 AOP가 적용되어 있는지 확인할 수 있으므로 실무에서는 Config파일에서 @Bean으로 등록하는 방법을 선호합니다.)

🌒 AOP 적용 전 의존관계

🌒 AOP 적용 후 의존관계

🌒 AOP 적용 전 전체관계

🌒 AOP 적용 후 전체관계

@Around를 통해 프로젝트 전체 메서드에 AOP가 적용되었으므로 컨테이너에 스프링 빈으로 등록되는 것들이 모두 프록시와 같이 등록된 것을 확인할 수 있습니다.

이러한 방식을 스프링에서 프록시(proxy)방식의 AOP라고 하며, 실제 코드로 proxy가 주입되는 출력으로 확인할 수 있습니다

기존의 MemberController의 생성자에 다음을 추가하고 실행을 통해 확인할 수 있습니다.

    @Autowired
    public MemberController(MemberService memberService){
        this.memberService = memberService;
        System.out.println("memberService = " + memberService.getClass());
    }
profile
1a2a3a4a5a

0개의 댓글