[Spring] AOP

나르·2022년 3월 5일
0

Spring

목록 보기
18/25

Spring에는 Spring Triangle이라고 불리는 3대 요소 IoC, PSA, AOP가 있습니다.
그 중 하나인 AOP(Aspect Oriented Programming), 관점 지향 프로그래밍에 대해 정리해보겠습니다.

What is AOP?

AOP, 즉 관점 지향 프로그래밍이란 어떤 로직을 기준으로 핵심적인 관점과 부가적인 관점으로 나누어서 보고 그 관점을 기준으로 각각 모듈화하는 것입니다.
여기서 핵심 관점은 주로 핵심 비즈니스 로직을, 부가적인 관점은 핵심 로직을 위한 데이터베이스 연결, 로깅 등을 말합니다.

class A {
    method a() {
        AAAA
        // a do something
        BBBB
    }
 
    method b() {
        AAAA
        // b do something
        BBBB
    }
}
 
class B {
    method c() {
        AAAA
        // c do something
        BBBB
    }
}

위 코드의 AAA,BBB 처럼 소스 코드상에서 여러군데서 사용되는 중복되는 코드들을 발견할 수 있는데, 이것을 흩어진 관심사 (Crosscutting Concerns)라 부르고, 이것들을 Aspect로 모듈화해 비즈니스 로직에서 분리하고 재사용하는 것이 AOP입니다.
즉 필요하지만 중복해서 작성해야하는 핵심 로직 이외의 코드는 외부로 분리하여, 핵심로직에만 집중할 수 있도록 하는 것 입니다.

AOP 의 주요 개념

termdescription
Aspect위에서 설명한 흩어진 관심사를 모듈화 한 것으로, 공통적으로 정의될 부가기능을 의미합니다.
TargetAspect가 적용되는 대상 (클래스, 메서드 .. )
JointPointAdvice가 적용 가능한 위치.
메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능
PointCutAdvice가 어떤 Joinpoint에 적용될 지를 나타냅니다.(어디에)
execution, @annotation 등 대상을 지정합니다.
AdviceAspect언제 핵심 코드에 적용할 지를 정의합니다.

PointCut 정규식

리턴타입메소드 이름파라미터
StringgetUser(String word1, String word2)
*com.example.demo.service..*(..)

..은 하위경로 전부라는 뜻입니다.

Advice의 종류

동작 시점description
Before메소드 실행 전
After메소드 실행 후
AfterReturning메소드가 정상적으로 실행된 후 (반환 후)
AfterThrowing예외가 발생한 후
Around메소드 호출 이전, 이후, 예외발생 등 모든 시점에서 동작
매개변수로 ProceedingJoinPoint를 받음

Around 의 value에는 어노테이션, 빈, 메소드 등을 등록할 수 있습니다

@Around("bean(userService)")
@Around("@annotation(Logging)")
@Around("execution(* com.example.demo.service.*Service.*(..))")

예제

Spring AOP를 사용하기위해 dependency를 추가해줍니다.

implementation 'org.springframework.boot:spring-boot-starter-aop'

Aspect 적용 대상을 지정할 어노테이션을 생성합니다.
@Target(ElementType.METHOD), @Retention(RetentionPolicy.RUNTIME) 을 추가해 적용대상과 생명주기를 설정해줍니다.

// Logging.java
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Logging {
}

이제 해당 어노테이션을 추가한 메소드에 어떤 기능을 추가할 것인지를 적용할 실제 Aspect를 구현합니다.
@Aspect@Component를 포함하지 않기때문에 @Component를 따로 추가해 Bean으로 등록해줘야합니다.
@Around()로 위에서 생성한 @Logging 어노테이션이 붙은 메소드의 실행 전후로 아래 로직을 실행하도록 합니다.

// LoggingAspect.java
@Slf4j
@Component
@Aspect
public class LoggingAspect {

    @Around("@annotation(com.example.demo.common.annotation.Logging)")
    public Object Logging(ProceedingJoinPoint joinPoint) throws Throwable {
        String nowTime = new SimpleDateFormat("YYYY. MM. DD HH:mm:ss")
        						.format(System.currentTimeMillis());
        Object ret = joinPoint.proceed();
        log.info(String.format("[%s] %s Called.", nowTime, joinPoint.getSignature().toShortString()));
        return ret;

    }
}

어노테이션이 같은 경로에 있는 것이 아니라면 전체 경로를 명시해줘야합니다.
그렇지 않는다면 java.lang.IllegalArgumentException: error Type referred to is not an annotation type 에러가 발생합니다.

이제 메서드에 @Logging 어노테이션을 추가하면, 생성한 Aspect가 잘 동작하는 것을 확인할 수 있습니다.

// service.java
@Service
public class AspectService {

	@Logging
    public void testAspect() {
        // do something
    }
}

Ref.

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://giron.tistory.com/70
https://onlyformylittlefox.tistory.com/15
https://engkimbs.tistory.com/746
https://congsong.tistory.com/25
https://devlog-wjdrbs96.tistory.com/398?category=882236

profile
💻 + ☕ = </>

0개의 댓글