java/AOP

Spring AOP 구현

일상코더 2023. 3. 29. 15:04

AOP란?

 

                AOP는 Aspect Oriented Programming의 약자로, 여러 객체에 공통으로 적용할 수 있는 기능을 

                분리해서 재새용성을 높여주 는 프로그래밍이다. 

 

                Spring에서는 주로 Logging, Caching, Auditing, Performace monitoring등에 주로 사용되며,

                런타임 시에 프록시 객체를 생성하여 공통 기능을 삽입하는 방식으로 동작한다.

 

 

Spring AOP 구현하기

               

1. Application 혹은 Config파일에 @EnableAspectJAutoProxy 어노테이션 추가하기

@EnableAspectJAutoProxy
@SpringBootApplication
public class LogPracApplication {

    public static void main(String[] args) {
        SpringApplication.run(LogPracApplication.class, args);
    }

}

 

2.  Aspect 파일 만들기

package com.example.logprac.log;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Map;
import java.util.stream.Collectors;

@Aspect
@Component
public class LoggingAspect {

    private static final Logger logger = LoggerFactory.getLogger(LoggingAspect.class);


    // onRequest메서드에 Controller 패키지 내의 모든 파일에 대해 PointCut으로 정의하겠다는 설정
    @Pointcut("within(com.example.logprac.controller..*)")
    public void onRequest(){

    }

    // 위에서 PointCut으로 정의한 onRequest 파일들에 대해 @Around시점(메서드 호출 시점)에 
    // requestLogging 메서드를 실행하겠다는 내용
    // Around: Around Advice를 줄여 @Around 어노테이션을 사용함
    @Around("com.example.logprac.log.LoggingAspect.onRequest()")
    public Object requestLogging(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();

        long start = System.currentTimeMillis();
        try{
            return proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
        }finally {
            long end = System.currentTimeMillis();
            logger.info("Request: {} {}: {} ({}ms)", request.getMethod(), request.getRequestURL(), paramMapToString(request.getParameterMap()), end - start);
        }
    }
    // 실제 객체(Controller의 메서드)를 호출하는 부분.
    // 공통 로직에서 하고 싶은 행동은 proceed 전후에 정의한다. 
    private String paramMapToString(Map<String, String[]> paraStringMap){
        return paraStringMap.entrySet().stream().map(entry -> String.format("%s : %s", entry.getKey(), Arrays.toString(entry.getValue()))).collect(Collectors.joining(", "));
    }


}

 

AOP 주요 용어와 어노테이션

 

                 - Aspect: 여러 객체에 공통으로 적용되는 기능. 트랜젝션, 보안 등

                 - Advice: 언제 공통로직을 핵심 로직에 적용할지를 정의하는 것

                 - JoinPoint: Advice를 적용 가능한 지점으로, 스프링은 프록시를 이용하여 AOP를 구현하기

                                      때문에 메서드 호출에 대한 JointPoint를 지원합니다. 

                 - PointCut: JoinPoint의 부분 집합으로, 실제 Advice가 적용되는 JoinPoint를 나타낸다.