AOP
  • aop : 메서드가 실행되기 전, 후의 여러 결과를 확인가능 => 로그를 남김
  • 이걸 지원하지 않는 프레임워크라면 전,후 처리할 내용들을 각 메서드마다 넣어줘야하므로 비지니스 로직의 일관성을 해침
  • 디버깅할 때 용이

 

(Rest컨트롤러)

@RestController
@RequestMapping("/api")
public class RestApiController {

    @GetMapping("/get/{id}")
    public String get(@PathVariable Long id, @RequestParam String name){
//        System.out.println("===get method===");
//        System.out.println("id: "+id);
//        System.out.println("name: "+name);    =>aop클래스에서 매개변수 로그를 찍어주므로 불필요

        return id+" "+name;
    }

    @PostMapping("/post")
    public User post(@RequestBody User user){
//        System.out.println("===post method===");
//        System.out.println(user);

        return user;
    }

    @Timer
    @DeleteMapping("/delete")
    public void delete() throws InterruptedException {
        Thread.sleep(1000*2);

    }

    @Decode
    @PutMapping("/put")
    public User put(@RequestBody User user){
        System.out.println("put");
        System.out.println(user);

        return user;}}

 

1.

@Aspect //aop를 정의하는 클래스에 할당
@Component
public class ParameterAop {

    @Pointcut("execution(* com.example.aop.controller..*.*(..))")   //aop가 적용되는 지점설정(여기서는 컨트롤러 내 모든 메서드)
    private void cut(){}

    @Before("cut()") //cut()메소드 실행되기 이전 이 메소드를 사용하라는 의미
    public void before(JoinPoint joinPoint) {    //joinPoint: 들어가는 지점에 대한 정보를 가지고 있는 객체
        MethodSignature methodSignature=(MethodSignature) joinPoint.getSignature(); //들어가는 지점의 메소드를 얻기 위한 작업
        Method method=methodSignature.getMethod();
        System.out.println(method.getName());   //메소드 이름

        Object[] args = joinPoint.getArgs(); //지점의 매개변수들로 이루어진 배열(타입을 모르니 object로.. )

        for (Object obj : args) {
            System.out.println("type: "+obj.getClass().getSimpleName());     //즉, 타입명
            System.out.println(obj);
        }
    }
    @AfterReturning(value = "cut()",returning = "returnObj")    //cut()이후에 값이 반환될 때 실행되고 그 반환값의 이름을 returnObj로 지정
    public void after(JoinPoint joinPoint,Object returnObj){
        System.out.println("return obj");
        System.out.println(returnObj);
    }
}

 

2. (어노테이션으로 영역표시)

@Target({ElementType.TYPE,ElementType.METHOD})  //Target: 이 어노테이션이 붙을 수 있는 애들 / TYPE: 클래스, 인터페이스
@Retention(RetentionPolicy.RUNTIME) //runtime때 사용한다는 의미
public @interface Timer {
//메소드 실행시간 => 서버의 부하/상태에 대한 로그를 남길 수 있음
@Aspect
@Component  //클래스 단위로 bean을 등록 (cf. @Bean : 클래스에x, bean등록 메서드에만 씀 => @Configuration : 여러개의 bean이 등록되는 클래스에 붙임)
public class TimerAop {
    @Pointcut("execution(* com.example.aop.controller..*.*(..))")
    private void cut(){}

    @Pointcut("@annotation(com.example.aop.annotation.Timer)")      //aop지점=타이머 어노테이션이 있는 곳
    private void enableTimer(){}

    //특정 메서드의 실행시간을 알아보자
    //그러려면 before 실행 부터 after실행 끝까지 걸린 시간을 재면 된다. => 시간을 공유해야하므로 before,after를 동시에 처리할 수 있는 around를 이용
    @Around("cut()&&enableTimer()")     //cut지점과 enableTimer지점을 동시에 만족하는 지점 => 즉, 컨트롤내에 timer어노테이션이 있는 지점
    public void around(ProceedingJoinPoint joinPointer) throws Throwable {
        //before 영역
        StopWatch stopWatch=new StopWatch();
        stopWatch.start();

        //실행
        Object result= joinPointer.proceed();   //메소드 실행!!! 실행되고 받은 리턴값도 받음(*메서드가 void형이면 암것도 안들어 감)

        //after영역
        stopWatch.stop();

        System.out.println("total time: "+stopWatch.getTotalTimeSeconds());
    }
}

3.

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Decode {
}
//base64로된 email 속성을 가진 요청을 put할 때 그걸 decode, encode하는 작업을 전후처리로 해보자
@Aspect
@Component
public class DecodeAop {
    @Pointcut("execution(* com.example.aop.controller..*.*(..))")
    private void cut(){}

    @Pointcut("@annotation(com.example.aop.annotation.Decode)")
    private void enableDecoder(){}

    @Before("cut()&&enableDecoder()")   //요청을 decode하여 객체에 넣어줌
    public void before(JoinPoint joinPoint) throws UnsupportedEncodingException {
        Object[] args=joinPoint.getArgs();

        for(Object obj:args){
            if(obj instanceof User){                //매개변수 객체가 user타입의 인스턴스이면 그걸 캐치하여
                User user=User.class.cast(obj);     // user로 캐스팅해준다.(Object->User)
                String base64Email=user.getEmail();

                String email=new String(Base64.getDecoder().decode(base64Email),"UTF-8");
                user.setEmail(email);
            }
        }
    }

    @AfterReturning(value = "cut()&&enableDecoder()",returning = "returnObj")
    public void afterReturning(JoinPoint joinPoint,Object returnObj){ //decode된 객체속성 가진 객체를 반환받는데, 이걸 다시 encode하여 응답으로 내보냄
        User user=User.class.cast(returnObj);

        String email=user.getEmail();
        String base64Email=Base64.getEncoder().encodeToString(email.getBytes());    //encode할 땐 encode()가 아닌 encodeToString임에 주의

        user.setEmail(base64Email);

    }
}

'BackEnd > 패캠' 카테고리의 다른 글

Validation  (0) 2022.01.21
AOP 실무 사례 알아보기  (0) 2022.01.20
Ioc, DI  (0) 2022.01.18
모범사례 - Object Mapper  (0) 2022.01.17
Response 내려주기 및 모범사례  (0) 2022.01.16