Spring AOP(Aspect Oriented Programming)

justindevcode·2022년 10월 1일
0

Spring AOP(Aspect Oriented Programming)

스프링은 컨테이너로 각 class를 붙이고 붙여서 작동이 된다.
그런데 class를 앞뒤로 붙이며 객체지향적으로 만든 아키텍처에 다른 수직방향으로 비슷한 기능들이 들어가야한다면 어떡할까?

그에 해당하는 기능들은 로깅,권한 보안, 트랜잭션, 메소드 시간측정등이 있다.
이런 기능들은 스프링 컨테이너에서 class 들이 Controller > Service > Repository 이런 의존관계에서 사이사이에 들어가야하며 Service1, Sercive2... 다양한 class의 메소드에 이곳저곳 필요하다.

public Long join(Member member) {
            memberRepository.findByName(member.getName()); 
            Optional<Member> result = memberRepository.findByName(member.getName());
            result.ifPresent(m -> {
                throw new IllegalStateException("이미 존재하는 회원입니다.");
            });

            시간측정
            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");
            }

가장 흔한 예시로 메소드의 시간 측정인데 핵심로직은 alidateDuplicateMember(member);, memberRepository.save(member);
이거 두줄이다.
근데 첫째로 뭐가 핵심로직인지 보이지도 않고 둘째로 다른 메소드에도 시간측정하려면 이렇게 다 써줘야한다는것.

그래서 횡단 관심모듈을 모아서 사이사이 끼워주는 방식을 AOP 측면/양상 지향적인 프로그래밍 이라고 하는것이다.

스프링은 이 방식을 프록시를 이용해서 구현해준다.

AOP는 JDK Dynamic Proxy를 통해 구현된다하는데

자바에서 제공하는 API (java.lang.reflect.Proxy)를 사용하며 인터페이스 기반으로 Proxy를 생성한다. 사용방법에는 여러가지가 있지만 개발자는 InvocationHandler 객체의 invoke()메서드를 오버라이딩하여 실제 객체의 정보를 받아오고 조작하면 된다.

라고한다...

CGlib proxy방식도 있다는데

CGlib는 MethodMatcher 객체를 사용하여 특정 메서드만 프록시화 하고, 나머지는 프록시를 거치지 않고 실제 객체 메서드를 호출하도록 만들 수 있다.
이게 가능한 이유는 CGlib는 인터페이스가 아닌 타겟 객체를 직접 상속받아 만들기 때문이다.

아직 잘은 모르겠다...
지금 이해하기 쉬운건 강의 자료에도 나왔던
이방식으로 AOP사용 어노테이션이 달린 애들을 부를때는 프록시가 앞에서 먼저 불리고 그 프록시가 진짜 메소드 앞뒤로 어노테이션되어있는 기능을 꼽아서 돌려주는것이다.


메소드 시간측정예시

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

지금 이 class 추가로 hello.hellospring하위에 모두를 적용한것


참고사이트
https://engkimbs.tistory.com/746
https://www.youtube.com/watch?v=Hm0w_9ngDpM
https://atoz-develop.tistory.com/entry/Spring-%EC%8A%A4%ED%94%84%EB%A7%81-AOP-%EA%B0%9C%EB%85%90-%EC%9D%B4%ED%95%B4-%EB%B0%8F-%EC%A0%81%EC%9A%A9-%EB%B0%A9%EB%B2%95
https://creamilk88.tistory.com/148
https://jiwondev.tistory.com/152
https://thalals.tistory.com/271

profile
("Hello World!");

0개의 댓글