주노 님의 블로그

[Spirng] 8. AOP(관점 지향 프로그래밍), 프록시 본문

공부/Spring

[Spirng] 8. AOP(관점 지향 프로그래밍), 프록시

juno0432 2024. 7. 30. 01:12

시작하기에 앞서

아래 내용은 김영한님의 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB접근기술에서 발췌한 내용입니다.

 

 

강의자료 일부를 발췌하는건 괜찮다고 하셨다.

무료강의로 지식을 공유해주신 김영한님께 다시한번 감사의 인사를 올리겠습니다

(_ _) (유료도 샀어요!)

 

AOP(Aspect Oriented Programming)란?

공통 관심 사항과 핵심관심 사항을 분리하는 것이다

 

1. 모든 메서드의 호출 시간을 측정하고 싶다면.

 

join 메서드 실행 시간 확인하기

public Long join(Member member)
{
    //메서드 실행 시간 확인하기
    long start = System.currentTimeMillis();
    try{
        //조건 추가 : 같은 이름이 있는 중복 회원은 안된다!
        Optional<Member> result = memberRepository.findByName(member.getName());
        //기존 멤버가 있으면, if null이 아니면<< 아래 에러를 던져준다
        // null일 가능성이 있으면 요즘은 optional로 감싼다. result.orElseGet() << 있으면? 없으면?
        result.ifPresent(m -> { throw new IllegalStateException("이미 존재하는 회원이다"); });

        validateDuplicateMember(member);
        memberRepository.save(member);
        return member.getId();
    }finally
    {
        long finish = System.currentTimeMillis();
        long timeMs = finish - start;
        System.out.println("join = " + timeMs + " ms");
    }
}

메서드 처음부분에 strat를

끝부분에 finish를 부착해서

시간을 측정한다.

 

 

이렇게 일일히 시간 조회 로직을 구현해야하나? >> AOP사용으로 해결할 수 있다.

 

시간 조회는 핵심 관심 사항(주요한 비즈니스 로직)이 아니다

메서드 실행 시간은 공통 관심 사항(여러 모듈이나 메서드에 발생하는 로직)이다.

핵심 관심 사항과 섞여있으면 유지보수에 어려움이 생긴다.

공통 관심사항은 별도의 공통 로직으로 만들기 어렵다.

 

AOP사용하는법

출처 김영한 스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB접근기술 중

 

공통 관심사항을 따로 분리하여 원하는 곳에 공통 관심사항을 적용케 하는것이다.

 

 

1. aop 클래스 생성

시간을 측정하는 로직인 TimeTraceAop를 만든다

 

//aop는 aspect 어노테이션 사용해야함
@Component
@Aspect
public class TimeTraceAop {
    //타게팅 하는법 : 내 프로젝트의 하위에 모두 적용한다.
    @Around("execution(* springProject.hello_spring..*(..)) && !target(springProject.hello_spring.member_management.SpringConfig)")
    public Object execute(ProceedingJoinPoint joinPoint) throws Throwable
    {
        long startTime = System.currentTimeMillis();
        System.out.println("START : " + joinPoint.toString());
        try {
            return joinPoint.proceed();
        } finally {
            long endTime = System.currentTimeMillis();
            long elapsedTime = endTime - startTime;
            System.out.println("End : " + joinPoint.toString() + " " + elapsedTime + "ms");
            System.out.println("------------------------------------------------------------------------------------------------------");
        }
    }
}

@Around는 타게팅 하는법으로 execution을 정의하면 내가 이 아래 패키지에서 AOP를 실행하겠다

이고, 이렇게만 하면 순환 참조 오류가 발생할 수 있는데, 그 이유는 SpringConfig 파일이 저 파일들 하위에 있을 경우

또 SpringConfig는 또 참조하게 된다

 

aop의 동작 원리

출처 : 인프런 김영한님의   스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB접근기술

aop를 사용하기전에는 컨트롤러에서 서비스를 호출할 때 의존관계로 호출했다

 

aop를 적용했을때

출처 : 인프런 김영한님의   스프링 입문 - 코드로 배우는 스프링 부트, 웹 MVC, DB접근기술

aop를 적용한다면 service는 프록시라는 가짜 객체를 생성한다

controller는 프록시를 호출하게 되고, joinPoint.proceed()를 사용하면

프록시는 실제 객체를 가르킨다

 

프록시란

실제 클래스를 상속 받아서 만들어지는 가짜 객체이다

하이버네이트가 자동생성을 하며, 사용하는 입장에서는 프록시인지, 아닌지 모른다

프록시 객체는 실제 데이터를 저장하지 않지만, 실제 데이터를 저장하는 참조만을 보관한다

 

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

 

memberService를 주입하는 단계에서 getClass를하면 

MemberServiceclass springProject.hello_spring.member_management.service.MemberService$$SpringCGLIB$$0

SpringCGLIB$$0<< 이런 프록시가 호출되게 된다.