티스토리 뷰

JVM/Spring

[Spring] AOP

글을 쓰는 개발자 2022. 4. 5. 17:10
반응형

AOP는 뭘까?


객체지향적인 코드를 다른 방식으로 구성한 방식. Call Exit 부분이 공통적으로 되어 있는 코드들이 있을 때 사용할 수 있는 기법

  • 대표적으로 사용하고 있는 @Transactional
  • 로그 찍을 때
  • 그 외

AOP는 어떤 것을 지원할까?


Before

특정 메소드가 실행되기 전에 대한 행동을 정의

After returning

특정 메소드가 정상적으로 실행되고 나서 일어날 행위를 정의

After throwing

특정 메소드가 실행되면서 에러가 났을 때 일어날 행위를 정의

After

특정 메소드가 실행되면서 정상적으로 끝나든 에러가 나듣 상관없이 일어날 행위를 정의

Around

특정 메소드 전 후의 행위를 정의

그렇다면 Spring AOP를 어떻게 정의해야 할까?

Spring AOP의 목표는 모든 AOP구현을 제공하는 것이 아니고, Spring IoC 컨테이너와 결합하여 애플리케이션 문제에 대한 해결책을 지원하는 것이 목적

AOP Proxy에 대해서

Proxy란?

간단하게 설명해서 어떠한 메소드가 실행되기 전에 임시 객체가 필요한데 이러한 역할을 Proxy가 해줍니다. Proxy가 없다면 실제 객체를 만들어줘야 하는데 이러한 것은 비용이 비쌀 수 있습니다. 이러한 것을 Proxying 해줘서 실제 사용하기 전까지 지연시킴으로써 비용을 절감하는 것이 주 목표입니다.

Pointcut 지정자들


execution: 적용할 메서드를 명시할 때 사용

execution([접근제어자][반환형][패키지](클래스)(메소드))

execution(public String com.example.ground.*.*(*))
// com.example.ground 패키지에 속해있고 접근제어자가 public이고, 파라미터가 1개인 모든 메서드

execution(* com.example.ground..*Service.*.(..))
// com.example.ground 패키지 안에 존재하고 Service로 끝나는 클래스이며, 접근제어자는 모두 허용, 파라미터는 0개 이상

execution(* other*(*,*))
// 메서드 이름이 other로 시작하고 파라미터가 2개인 메서드

within: 해당 패키지 또는 클래스 내의 모든 메서드

within(com.example.ground.SimpleService)
// SimpleService 모든 메서드

within(com.example.ground.*)
// com.example.ground 패키지의 모든 메서드

within(com.example.ground..*)
// com.example.ground 패키지 및 하위 패키지의 모든 메서드
//--aspect
//----SimpleService
//----OtherService
//----nested
//------NestedService
@Service
@RequiredArgsConstructor
@Transactional
public class SimpleService {

    private final OtherService otherService;

    private final NestedService nestedService;

    public String simple() {
        otherService.other("1", "2");
        nestedService.nested();
        return "ok";
    }

}
    @Before("within(com.ground..aspect..*)")
    public void invoke3(JoinPoint joinPoint) throws Throwable{
        log.info("---> in {}", joinPoint.getSignature());

    }

    ///
OUTPUT

: ---> in String com.ground.aspect.SimpleController.simple()
: ---> in String com.ground.aspect.SimpleService.simple()
: transactional - start
: nested-start
: ---> in void com.ground.aspect.OtherService.other(String,String)
: nested-end
: transactional - end
: ---> in void com.ground.aspect.nested.NestedService.nested()
    @Before("within(com.ground..aspect.*)")
    public void invoke3(JoinPoint joinPoint) throws Throwable{
        log.info("---> in {}", joinPoint.getSignature());

    }
OUTPUT
: ---> in String com.ground.aspect.SimpleController.simple()
: ---> in String com.ground.aspect.SimpleService.simple()
: transactional - start
: nested-start
: ---> in void com.ground.aspect.OtherService.other(String,String)
: nested-end
: transactional - end

annotation

언급한 어노테이션을 사용한 메서드에 대해서 적용할 때 사용

    @Around("@annotation(org.springframework.transaction.annotation.Transactional)")
    public Object invoke(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("transactional - start");
        Object proceed = joinPoint.proceed();
        log.info("transactional - end");
        return proceed;
    }
    @Transactional
    public String simple() {
        return "ok";
    }
: transactional - start
: transactional - end

Pointcut 활용

pointcut도 여러개를 만들고 또 하나의 Pointcut에서 조합할 수 있습니다.

@Pointcut("execution(public * *(..)")
private void allPointcut() {}

@Pointcut("execution(public * method1(..)")
private void method1Pointcut() {}

// method1 
@Pointcut("allPointcut() && method1Pointcut()")
private void andCombined() {}

// other method
@Pointcut("allPointcut() && !methodPointcut()")
private void otherCombined() {}

Example

@Before("left() && !right()")
    public void invoke4(JoinPoint joinPoint) throws Throwable {
        log.info("--> combine in --> {}", joinPoint.getSignature());
    }
2022-04-05 18:01:14.896  INFO 34279 --- [nio-8000-exec-2] com.ground.aspect.SimpleAspect           : ---> in String com.ground.aspect.SimpleController.simple()
2022-04-05 18:01:14.908  INFO 34279 --- [nio-8000-exec-2] com.ground.aspect.SimpleAspect           : transactional - start
2022-04-05 18:01:14.908  INFO 34279 --- [nio-8000-exec-2] com.ground.aspect.SimpleAspect           : ---> in String com.ground.aspect.SimpleService.simple()
2022-04-05 18:01:14.910  INFO 34279 --- [nio-8000-exec-2] com.ground.aspect.SimpleAspect           : transactional - start
2022-04-05 18:01:14.910  INFO 34279 --- [nio-8000-exec-2] com.ground.aspect.SimpleAspect           : nested-start
2022-04-05 18:01:14.910  INFO 34279 --- [nio-8000-exec-2] com.ground.aspect.SimpleAspect           : ---> in void com.ground.aspect.OtherService.other(String,String)
2022-04-05 18:01:14.911  INFO 34279 --- [nio-8000-exec-2] com.ground.aspect.SimpleAspect           : nested-end
2022-04-05 18:01:14.911  INFO 34279 --- [nio-8000-exec-2] com.ground.aspect.SimpleAspect           : transactional - end
2022-04-05 18:01:14.911  INFO 34279 --- [nio-8000-exec-2] com.ground.aspect.SimpleAspect           : transactional - end
    @Before("left() && right()")
    public void invoke4(JoinPoint joinPoint) throws Throwable {
        log.info("--> combine in --> {}", joinPoint.getSignature());
    }
2022-04-05 18:02:28.340  INFO 34339 --- [nio-8000-exec-1] com.ground.aspect.SimpleAspect           : ---> in String com.ground.aspect.SimpleController.simple()
2022-04-05 18:02:28.348  INFO 34339 --- [nio-8000-exec-1] com.ground.aspect.SimpleAspect           : transactional - start
2022-04-05 18:02:28.348  INFO 34339 --- [nio-8000-exec-1] com.ground.aspect.SimpleAspect           : ---> in String com.ground.aspect.SimpleService.simple()
2022-04-05 18:02:28.348  INFO 34339 --- [nio-8000-exec-1] com.ground.aspect.SimpleAspect           : --> combine in --> String com.ground.aspect.SimpleService.simple()
2022-04-05 18:02:28.350  INFO 34339 --- [nio-8000-exec-1] com.ground.aspect.SimpleAspect           : transactional - start
2022-04-05 18:02:28.350  INFO 34339 --- [nio-8000-exec-1] com.ground.aspect.SimpleAspect           : nested-start
2022-04-05 18:02:28.350  INFO 34339 --- [nio-8000-exec-1] com.ground.aspect.SimpleAspect           : ---> in void com.ground.aspect.OtherService.other(String,String)
2022-04-05 18:02:28.351  INFO 34339 --- [nio-8000-exec-1] com.ground.aspect.SimpleAspect           : nested-end
2022-04-05 18:02:28.351  INFO 34339 --- [nio-8000-exec-1] com.ground.aspect.SimpleAspect           : transactional - end
2022-04-05 18:02:28.352  INFO 34339 --- [nio-8000-exec-1] com.ground.aspect.SimpleAspect           : transactional - end
```

Patterns

  • 모든 public 메서드
execution(public * *(..))
  • 특정이름으로 시작하는 모든 메서드
execution(* hello*(..))

hello로 시작하는 모든 메서드를 의미

  • 특정 클래스 하위 모든 메서드
execution(* com.go.to.service.MyService.*(..))
  • 특정 패키지 내의 모든 메서드 (내부 패키지 제외)
execution(* com.go.to.service.*.*(..))
within(com.go.to.service.*)
  • 특정 패키지 및 하위 패키지 내의 모든 메서드
execution(* com.go.to.service..*.*(..))
within(com.go.to.service..*)
  • 특정 Bean method 들Service로 끝나는 Bean 탐색
  • bean(*Service)

Spring AOP 과 AspectJ의 차이점


위에서 언급한 것 처럼 Spring AOP는 Spring IoC Container를 통한 AOP 사용이 주 목적이며 AOP 그 자체가 아닙니다.
그리고 Spring AOP는 런타임 Weaving을 사용하고, AspectJ는 컴파일 시점 Weaving을 사용합니다.
적용 범위도 Spring AOP는 메서드 한정이지만, AspectJ의 경우에는 다양하게 적용할 수 있습니다.

 

반응형

'JVM > Spring' 카테고리의 다른 글

[Hikari CP] 光 살펴보기 - 2  (0) 2022.04.27
[Hikari CP] 光 살펴보기 - 1  (0) 2022.04.26
[Spring] @Transactional  (0) 2022.04.02
[JAVA][Spring][dirty checking] 준영속 엔티티 대해서  (0) 2021.12.09
[JPA] JPA N+1 문제  (0) 2021.12.03
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함