AOP 실습1

이정연·2023년 2월 13일
0

예제

목록 보기
2/2

Aspect1.java

@Slf4j
@Aspect
public class Aspect1 {

    @Around("execution(* start.aop.order..*(..))")
    public Object logging(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("log -> {}", joinPoint.getSignature());
        return joinPoint.proceed();
    }
}
  • @Around 애너테이션의 값 → execution( start.aop.order..(..))는 포인트컷이 된다.
  • execution( start.aop.order..(..))은 AspectJ 포인트컷 표현식임
  • @Around의 메서드인 loggin은 어드바이스가 됩니다.
  • joinPoint.getSignature() 로인해 로그가 남게된다.
 log -> void start.aop.order.OrderService.orderItem(String)
  • joinPoint.proceed(); 은 다음 어드바이스를 실행시킨다.

@Slf4j
@SpringBootTest
@Import(Aspect1.class) < -----Aspect1을 스프링 Bean으로 등록함.
public class AopTest {
	.
    .
    .
}

Aspect2.java

  • @Around에 포인트컷 표현식을 직접 넣을 수 있지만 @Pointcut 애너테이션을 사용해서 분리할 수 있다.
  • 메서드 반환 타입은 void여야 한다.
  • private & public 등의 접근 제어자는 내부에서만 사용하면 private, 다른 애스펙트에서 참고하면 public으로 사용하면 된다.
@Slf4j
@Aspect
public class Aspect2 {

    @Pointcut("execution(* start.aop.order..*(..))")
    private void allOrder(){} <------------

    @Around("allOrder()")
    public Object logging(ProceedingJoinPoint joinPoint) throws Throwable {
				log.info("log -> {}", joinPoint.getSignature());
        return joinPoint.proceed();
    }
}

Aspect3.java

  • 로그 출력 기능에 추가로 트랜잭션이 동작하는 것처럼 로그 남기는 기능을 추가합니다.
  • 스프링에서 AOP는 메서드 시작전에 만 joinpoint로 할 수 있다.
@Slf4j
@Aspect
public class Aspect3 {

    @Pointcut("execution(* start.aop.order..*(..))")
    private void allOrder(){}

    @Pointcut("execution(* *..*Service.*(..))")
    private void allService(){}

    @Around("allOrder()")
    public Object logging(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("log -> {}", joinPoint.getSignature());
        return joinPoint.proceed();
    }

    @Around("allOrder() && allService()")
    public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable {

        try {
            log.info("트랜잭션 시작 -> {}", joinPoint.getSignature());
            Object result = joinPoint.proceed();
            log.info("트랜잭션 커밋 -> {}", joinPoint.getSignature());
            return result;
        } catch (Exception e) {
            log.info("트랜잭션 롤백 -> {}", joinPoint.getSignature());
            throw e;
        } finally {
            log.info("리소스 릴리즈 -> {}", joinPoint.getSignature());
        }
    }
}
  • allService()의 포인트컷은 타입 이름 패턴이 Service를 대상으로 하는데 이름이 Service로 끝나는 것을 대상으로 한다.
    Servi과 같은 패턴도 사용 가능함.
  • OrderService에는 logging과 doTransaction이 모두 적용됩니다.
  • OrderRepository에는 logging만 적용됩니다.

포인트 컷 참조

  • 포인트컷을 공용으로 사용하기 위해 외부 클래스로 외부에 모아둘 수 있다.
  • 따라서 접근 제한자는 public이어야 한다.
public class Pointcuts {
    @Pointcut("execution(* start.aop.order..*(..))")
    public void allOrder(){}

    @Pointcut("execution(* *..*Service.*(..))")
    public void allService(){}

    @Pointcut("allOrder() && allService()")
    public void orderAndService(){}
}

Aspect4.java + 포인트컷 참조

  • Aspect3와 결과는 같다.
@Slf4j
@Aspect
public class Aspect4 {

    @Around("start.aop.order.aop.Pointcuts.allOrder()")
    public Object logging(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("log -> {}", joinPoint.getSignature());
        return joinPoint.proceed();
    }

    @Around("start.aop.order.aop.Pointcuts.orderAndService()")
    public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable {

        try {
            log.info("트랜잭션 시작 -> {}", joinPoint.getSignature());
            Object result = joinPoint.proceed();
            log.info("트랜잭션 커밋 -> {}", joinPoint.getSignature());
            return result;
        } catch (Exception e) {
            log.info("트랜잭션 롤백 -> {}", joinPoint.getSignature());
            throw e;
        } finally {
            log.info("리소스 릴리즈 -> {}", joinPoint.getSignature());
        }
    }
}

Aspect5.java

  • 어드바이스는 기본적으로 순서를 보장하지 않음
  • @Aspect 적용 단위로 @Order 애너테이션을 적용하면 순서를 보장할 수 있다.
  • @Order 는 클래스 단위로 적용할 수 있기때문에 별도의 클래스로 애스펙트를 분리해야 한다.
@Slf4j
@Aspect
public class Aspect5 {

    @Aspect
    @Order(2)
    public static class LogAspect {
        @Around("start.aop.order.aop.Pointcuts.allOrder()")
        public Object logging(ProceedingJoinPoint joinPoint) throws Throwable {
            log.info("log -> {}", joinPoint.getSignature());
            return joinPoint.proceed();
        }
    }

    @Aspect
    @Order(1)
    public static class TxAspect {
        @Around("start.aop.order.aop.Pointcuts.orderAndService()")
        public Object doTransaction(ProceedingJoinPoint joinPoint) throws Throwable {

            try {
                log.info("트랜잭션 시작 -> {}", joinPoint.getSignature());
                Object result = joinPoint.proceed();
                log.info("트랜잭션 커밋 -> {}", joinPoint.getSignature());
                return result;
            } catch (Exception e) {
                log.info("트랜잭션 롤백 -> {}", joinPoint.getSignature());
                throw e;
            } finally {
                log.info("리소스 릴리즈 -> {}", joinPoint.getSignature());
            }
        }
    }
}

@Order(1)이 먼저 실행되고 그다음 @Order(2)가 실행 된다.

@Import({Aspect5.LogAspect.class, Aspect5.TxAspect.class})
@SpringBootTest
public class AopTest {
}

Import 방법이 조금 다르다.

profile
반갑습니다.

0개의 댓글