Spring, AOP

AOP

  • 흩어진 코드를 한 곳으로 모은다

[나온 배경]

똑같은일을 하는데 여러곳에 흩어져있는 코드가 있을때, 그 코드를 수정하려면 사용한곳을 모두 찾아서 수정해야한다

  • 예제
@GetMapping("/owners/find")
public String initFindForm(Map<String, Object> model) {
      // AOP 사용 전 , 불편하다!!!
      StopWatch stopWatch = new StopWatch();
      stopWatch.start();

  model.put("owner", new Owner());

      // AOP 사용 전 , 불편하다!!!
      stopWatch.stop();
      System.out.println(stopWatch.prettyPrint());

  return "owners/findOwners";
}


[해결]

똑같은 일을 하는 코드를 한데 묶어버린다

AOP 구현 방법

  • 컴파일
    • ex) A.java –(AOP끼어넣는다)–>A.class (기능이 생겨난다)
  • 바이트코드 조작
    • A.java -> A.class –(AOP)–> 메모리 (기능이 생겨난다)
  • 프록시 패턴 (스프링 AOP가 사용하는 방법)
    • 기존코드 건들지 않고 새기능 추가하는방법
    • 참고로 어떤 내용인지만 알고있으면된다
@Test
public void testPay(){
    // 스프링 부트가 하는일 ->
    // Cash만 만들어놔도 프록시 패턴인 CashPerf가 자동으로 빈에 등록되고 Cash대신에 사용된다
    Payment cashPerf1 = new CashPerf(); // stopwatch기능 있음 , proxy패턴 !
    Payment cashPerf2 = new Cash();     // stopwatch기능 없음
    Store store = new Store(cashPerf1);
    store.buySomething(100);
}

AOP 적용 예제

@LogExecutionTime 으로 메소드 처리 시간 로깅하기

  • @LogExecutionTime 에노테이션 (어디에 적용할지 표시 하는 용도) 생성

  • LogExecutionTime.java

// 이 에노테이션을 메소드에 사용하겠다
@Target(ElementType.METHOD)
// 이 정보를 언제까지 유지할것인가 , 런타임까지 유지한다
@Retention(RetentionPolicy.RUNTIME)
public @interface LogExecutionTime {
}
  • 해당 애노테이션 정의

@Component // Bean으로 등록해준다
@Aspect
public class LogAspect {
    Logger logger = LoggerFactory.getLogger(LogAspect.class);

    // 프록시패턴을 기반으로 동작한다
    // ★ LogExecutionTime 애노테이션이 하는일을 설정해준다 ★
    @Around("@annotation(LogExecutionTime)")
    public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
        // ProceedingJoinPoint : 애노테이션이 붙어 있는 메소드가 실행되면
        // 아래 일을 실행하겠다.
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();

        // 애노테이션이 붙어있는 메소드를 실행해라
        Object proceed = joinPoint.proceed();

        stopWatch.stop();
        logger.info(stopWatch.prettyPrint());

        return proceed;
    }
}
  • 사용
@GetMapping("/owners/{ownerId}/edit")
@LogExecutionTime
public String initUpdateOwnerForm(@PathVariable("ownerId") int ownerId, Model model) {
  Owner owner = this.owners.findById(ownerId);
  model.addAttribute(owner);
  return VIEWS_OWNER_CREATE_OR_UPDATE_FORM;
}

Reference

  • [inflearn] 예제로 배우는 스프링 입문 백기선님 강좌