Spring AOP 심화 글

SionBackEnd·2022년 8월 17일
0

Spring(봄)

목록 보기
8/22
post-thumbnail

AOP

내가 이해한 AOP 심화 기능
AOP는 단순하게 귀찮음을 덜하게 만들기 위해서 만들어졌다고 볼수있다.
모든 메서드의 성능 테스트를 부탁하는 요청이나, 트랜젝션, 보안이나 이러한 공통적인 기능들을 한번의 코드 작성으로 모든 메서드 또는 내가 지정한 많은 메서드에 적용시켜 중복되는 코드를 줄일수 있는 방법이다.

애플리케이션에는 핵심기능과 부가기능 이렇게 두가지가 하나의 객체로 들어가게 되는데 서비스를 실행하면 핵심 기능과 부가 기능이 함께 실행된다. 핵심기능은 중복이 되지않지만, 부가기능같은 경우는 여러곳에 공통적으로 적용되기 때문에 번거롭고 중복 코드가 생기게 된다. 따라서 핵심 기능 객체에 공통적으로 적용되는 부가기능을 AOP를 사용하여 적어주는것이다.

  • 핵심 기능(Core Concerns) : 업무 로직을 포함하는 기능
  • 부가 기능(CROSS-CUTTING CONCERNS) : 핵심 기능을 도와주는 부가적인 기능
    - 로깅, 보안, 트랜잭션 등이 있다.

OOP와 AOP 차이점

OOP는 중복되는 코드를 메소드로 만들어서 메소드가 필요한곳에 다 적어주는 것이다.
하지만 이 중복 코드를 1000메소드에 적어주어야 한다면, 모든 기능들에 다 적어야하는 공통 메소드라면 어떻게 해야 할까? 일일이 다 적는다는건 유지보수측면 뿐만 아니라 너무나도 귀찮다.
AOP는 이러한 기능을 하기 위해서 등장했다고 보면된다.

AOP는 OOP를 대신하는 개념이아니라, OOP를 더욱 OOP스럽게 사용할 수 있도록 도와주는 개념이다.
OOP의 한계를 극복하기 위한 패러다임이다.
AOP는 중복되는 코드를 한번만 적고 그것을 어디에 적용시킬지, 모든 기능에 다 적용시킬지 정해서 OOP처럼 메소드를 각각 다 적어줄 필요가 없다. 훨씬 수월하다. 또한 유지보수에도 더욱 용이하다. (공통으로 들어가는 로직 하나만 수정하면 되니까!)

ex) 로깅, 성능테스트, 보안, 트랜젝션 등등 공통적으로 들어가는 기능

Advice

  • Aspect를 언제 핵심 코드에 적용할지를 정의함
  • 부가 기능에 해당된다.
  • 특정 조인 포인트에서 애스펙트에 의해 취해지는 조치이다.

Adivce는 기본적으로 순서를 보장하지 않는다.
순서를 지정하고 싶으면 @Aspect 적용 단위로
org.springframework.core.annotation.@Order 애너테이션을 적용해야함

  • 어드바이스 단위가 아니라 클래스 단위로 적용가능
  • 하나의 애스펙트에 여러 어드바이스가 존재하면 순서를 보장 받을 수 없다.
    -> 애스펙트를 별도의 클래스로 분리해야함

종류

Adivce의 종류는

  • Before
  • After returning
  • After throwing
  • After(finally)
  • Around
    가 있지만 가장 중요하고 강력한 Around에 대해서 알아보자

Around

  • 메서드 호출 전후에 수행하며 가장 강력한 어드바이스이다.
    - 조인 포인트 실행 여부 선택, 반환 값 변환, 예외 변환 등이 가능하다.
  • 메서드 실행 전 & 후, 예외 발생 시점에 공통 기능을 실행시킨다.
  • 조인 포인트 실행 여부 선택 - joinPoint.proceed()
  • 전달 값 변환 - joinPoint.proceed(args[])
  • 반환 값 변환
  • 예외 변환
  • try ~ catch ~ finally 가 들어가는 구문 처리 가능하다.
  • 어드바이스의 첫 번째 파라미터는 ProceedingJoinPoint를 사용해야 한다.
  • proceed()를 통해 대상을 실행한다.
  • proceed()를 여러번 실행 가능하다.

주의점

Around는 가장 강력한 어드바이스이며 대부분의 기능을 제공하지만, 타겟 등 고려해야할 상항이 있을 때 정상적으로 작동이 되지 않는 경우가 있다.

  • @Before, @After와 같은 어드바이스는 기능은 적지만 원하는대로 작동되고 코드도 단순하다.
  • 각 애너테이션만 봐도 타겟 실행 전에 어떤 일을 하는지 명확하게 알 수 있다.
  • 좋은 설계는 @Around만 사용해서 모두 해결하는 것보다는 제약을 가지더라도 실수를 미연에 방지하는것이다.
  • 제약을 두면 문제 자체가 발생하지 않게 되며, 역활이 명확해진다.

Pointcut

포인트컷은 관심 조인 포인트를 결정하므로 어드바이스가 실행되는 시기를 제어할 수 있다.
AspectJ는 포인트컷을 편리하게 표현하기 위해 특별한 표현식을 제공한다.

@Pointcut("execution(* transfer(..))") // 포인트컷 표현식
private void anyOldTransfer() {} // 포인트컷 서명

포인트컷 표현식은 AspectJ Pointcut expression => AspectJ가 제공하는 포인트컷 표현식을 줄여서 표현하는 것이다.

포인트컷 지시자

포인트컷 표현식은 execution같은 포인트컷 지시자(Pointcut Designator, PCD)로 시작한다.

종류

종류설명
execution메서드 실행 조인트 포인트를 매칭한다. 스프링 AOP에서 가장 많이 사용하며, 기능도 복잡하다.
within특정 타입 내의 조인 포인트를 매칭한다.
args인자가 주어진 타입의 인스턴스인 조인 포인트
this스프링 빈 객체(스프링 AOP 프록시)를 대상으로 하는 조인 포인트
targetTarget 객체(스프링 AOP 프록시가 가르키는 실제 대상)를 대상으로 하는 조인 포인트
@target실행 객체의 클래스에 주어진 타입의 애너테이션이 있는 조인 포인트
@within주어진 애너테이션이 있는 타입 내 조인 포인트
@annotation메서드가 주어니 애너테이션을 가지고 있는 조인 포인트를 매칭
args전달된 실제 인수의 런타임 타입이 주어진 타입의 애너테이션을 갖는 조인 포인트
been스프링 전용 포인트컷 지시자이고 빈의 이름으로 포인트컷을 지정한다.

execution을 가장 많이 사용하고 나머지는 자주 사용하지 않습니다.

Pointcut 표현식 결합

포인트컷 표현식은 &&, ||, !를 사용하여 결합이 가능하다.
이름으로 Pointcut 표현식을 참조할 수 있다.

@Pointcut("execution(public * *(..))")
private void anyPublicOperation() {} // (1)

@Pointcut("within(com.xyz.myapp.trading..*)")
private void inTrading() {} // (2)

@Pointcut("anyPublicOperation() && inTrading()")
private void tradingOperation() {} // (3)
  • anyPublicOperation은 메서드 실행 조인 포인트가 공용 메서드의 실행을 나타내는 경우 일치하다.
  • in Trading 메서드 실행이 거래 모듈에 있는 경우에 일치하다.
  • tradingOperation은 메서드 실행이 거래 모듈의 공개 메서드를 나타내는 경우 일치하다.

JoinPoint

JoinPoint는 AOP 적용 위치를 나타낸다.

  • 적용 가능 지점(조인 포인트): 생성자, 필드 값 접근, static 메서드 접근, 메서드 실행
  • AOP 를 수행하는 메소드는 이 JoinPoint 인스턴스를 인자로 받게 된다.
  • JoinPoint 인스턴스에서 조인 포인트 지점의 정보를 얻어내야 한다.

JoinPoint 인터페이스의 주요 기능

  • JoinPoint.getArgs() : JoinPoint에 전달된 인자를 배열로 반환한다.

  • JoinPoint.getThis() : AOP 프록시 객체를 반환한다.

  • JoinPoint.getTarget() : AOP가 적용된 대상 객체를 반환한다.

    • 클라이언트가 호출한 비즈니스 메소드를 포함하는 비즈니스 객체를 반환한다.
  • JoinPoint.getSignature() : 조언되는 메서드에 대한 설명을 반환한다.

    • 클라이언트가 호출한 메소드의 시그니처(리턴타입, 이름, 매개변수) 정보가 저장된 Signature 객체를 반환한다.
    • Signature : 객체가 선언하는 모든 연산은 연산의 이름, 매개변수로 받아들이는 객체들을 시그니처라고 한다.
    • Signature가 제공하는 메서드
      • String getName() : 클라이언트가 호출한 메소드의 이름을 반환한다.
      • String toLongString() : 클라리언트가 호출한 메소드의 리턴타입, 이름, 매개변수를 패키지 경로까지 포함해서 반환한다.
      • String toShortString() : 클라이언트가 호출한 메소드 시그니처를 축약한 문자열로 반환한다.
  • JoinPoint.toString() : 조언되는 방법에 대한 유용한 설명을 인쇄한다.

ProceedingJoinPoint 인터페이스의 주요 기능

  • proceed() : 다음 어드바이스나 타켓을 호출한다.
profile
많은 도움 얻어가시길 바랍니다!

0개의 댓글