SpringBoot

Spring AOP -강의정리

요는 2022. 3. 22. 12:06
더보기

AOP가 뭔지 가볍게 살펴볼 예정


1. Aspect Oriented Programming

0. 개요

어느 한 함수 처리에 걸리는 시간 측정하고 싶다

- 실제 서비스의 흐름과는 별개 

   ex )  로그 남기기 / 서비스 제공을 위한 기능

- 서로 직접적인 연관은 없음

   ex ) 게시글 읽기, 게시판 목록 불러오기 -> 연관성 없지만 같은 기능의 일부분 구현해줘야됨.

         -> 동일한 코드 반복해서 들어가게됨-> 좋은 코드 아님

 

 

1. AOC

: 서로 다른 비즈니스 로직이 공통적으로 가지는 관심에 대해 고민하는 개발 지향

- log를 남기는 기능 -> 어떤 비즈니스 로직에 포함되어야함

- but 반복되면 소스코드 복잡, 지저분해짐

 

 

3가지 시나리오 살펴보기
- 실행할 때 걸리는 시간
- 실행할 때 들어오는 함수의 인자
- 돌아나가게 되는 함수의 반환값
을 동일하게 logging 하는 것이 이번강의 목표

 


2. AOP의 기본 개념

0. 개요

카페 case

- 카페 생성, 게시판 생성, 게시글 생성 각각 buisness logic

- 어떤 user가 사용할지, 걸리는 과정, 시간, db 넣는 과정 필요 

- 횡단 관심 해소하기 위해 등장한 것이 AOP

횡단 관심(Cross Cutting Concerns) : 여러가지의 핵심 기능 사이를 횡단

 

 

 

1. AOP 기본개념

일반적인 프로그램의 flow

실행 -> class 객체화되어 생성 -> (함수,예외갖음)-> 서로 기능가진 객체들이 상호작용하며 프로그램 기능 읽어냄 -> 종료시 까지 진행

Logging Aspect : logging 하고 싶은 관점 
Join Points : 관점이 적용될 수 있는 지점 
      - 함수 실행 이전
      - 함수 반환 이후
      - 예외 발생 시

 

 

Pointcut : Join Point 정의 하기 위해 지정 -> 규칙을 가진 문자열로 표현됨
Advice : logging aspect 관점에서 실제로 실행 될 함수

logging aspect와 advice, pointcut 을 코드로 적용 할 것

 

 


실습 1 - Around PointCut 생성

aspect 패키지 작성

annotation의 형태로 aspect 작성

 

1.  annotation 만들기

LogExecutionTime

더보기

@Target(ElementType.METHOD)

-  어떠한 것에 붙을 수 있는 annotation인지 알려줌

-  elementType. Method -> 함수에 붙을 수 있음

 

더보기

@Retention(RetentionPolicy.RUNTIME)

annotation 이 실제 기능에 연관될지, 컴파일 과정에 필요할 지, 그냥 표기용도로 사용 될 수 있음

- annotation이 어느 시점까지 컴퓨터상에 존재 할 것인지

- RetentionPolicy  - SOURCE : 소스코드 상에 작성 위해

                        - CLASS : 컴파일러에선 읽어들임, 실행중엔 VM에 들어가지 않음( 실행 할 땐 상관없는 annotation)

                        - RUNTIME : annotation이 정상적으로 실행 할 때 작동됨

 

 

 

2. annotation에 따라 작동할 aspect만들기

1) build.gradle

spring AOP는 dependency를 추가해야됨

더보기

implementation 'org.springframework.boot:spring-boot-starter-aop'

2) LoggintAspect

더보기

@Component

@Aspect

- @Component : bean의 일종으로 선언해야됨. 

- @Aspect : springboot에선 aspectj를 사용해서 AOP구현

- log 작성

 

 

3. LogExecutionTime에 대한 advice

OOP의 관점에서 하나의 객체에서는 하나의 역할을 하는게 좋으니 하나의 advice를 한 객체 안에서 정의

public Object logExecutionTime() throws Throwable{
	long startTime = System.currentTimeMillis();
    
    long execTime = System.currentTimeMillis() - startTime;
    logger.trace("method executed in {}", execTime);
    return null;
}

 

실행시간 측정 long startTime, long execTime

 

4.  advice pointcut

advice를 실제로 프로그램 상에 적용하기 위해 어떤 join point에 들어갈 지 필요한 pointcut

더보기

@Around(value ="@annotation(LogExecurtionTime)") 

1) @Around() 

: 특정 pointcut을 기준으로 주위에서 작동하게 되는 함수일 때 사용

- LogExecutionTime이라는 annotation 주변에서 일어나는 일이 아래 함수임을 선언.

- aspectj에서 요구하는 정규 표현식으로 작성 해야됨

 

2) 함수 안에 인자 넣기

ProceedingJoinPoint 

public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable{
    long startTime = System.currentTimeMillis();
    Object proceed = joinPoint.proceed();
    long execTime = System.currentTimeMillis() - startTime;
    logger.trace("method executed in {}", execTime);
    return proceed;
}

일반적인 joinpoint : 일어났을 때, 그 시점

더보기

joinPoint.proceed() 

- proceed 함수가 추가된 확장형 : 다음에 실행될 advice, 함수 자체로 진행을 한다는 뜻

- 결과값을 받고 그 사이에서 얼마나 시간이 흘렀는지 log로 작성, 돌아온 proceed 반환하게 함.


실습2 - Before PointCut 생성

LogArguments 생성

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

 

1. aspect 에서 @Before 사용

@Before(value="@annotation(LogParameters)")
public void logParameters(JoinPoint joinPoint){
	MethodSignature  methodSignature =(MethodSignature) joinPoint.getSignature();
    //어떤 함수 사용했는지
    logger.trace("method description : [{}]", methodSignature.getMethod()); 
    //함수의 실제이름
    logger.trace("method name :[{}]", methodSignature.getName()); 
    //함수가 실행된 class에 대한 값
    logger.trace("declaring class:[{}]",methodSignature.getDeclaringType()); 
    
    Object[] arguments = joinPoint.getArge();
    if(arguments.length==0){
    	logger.trace("no arguments"); } //인자가 없는경우
    for(Object argument : arguments){
    	logger.trace("argument :[{}]", argument);}
    
}

 

1)

더보기

@Before(value="@annotation(LogArguments)")

- 실행되기 전에만 잠시 들렸다 돌아가는 함수

- argument를 log하겠다 라고 함 

 

 

void인 이유 

- around pointcut : 실행되기전, 실행된  후 둘다 접근-> 반환했어야 하는 값을 반환해야됨.

- before pointcut : 특정 시점 이전에 무슨일 있었는지 확인하는것 -> 반환할 것이 없음

 

2)

더보기

MethodSignature  methodSignature =(MethodSignature) joinPoint.getSignature();

- Signature : 함수가 어떤 모양 갖고 있는지 

- joinPoint에서 signature 가져옴

- methodSignature가 joinPoint 인터페이스자체에 안들어가 있어서 형변환 해야됨

 

3)

더보기

Object[] arguments = joinPoint.getArgs();

- joinpoint가 담고 있는 것중 일부가 우리가 찾고 싶음: argument

- 함수에서 들어온 인자들이 argument 변수 안에 들어옴

 

2. RestController에 추가

LogParameters


실습3 - After PointCut 생성

LogReturn 생성

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

 

1. @AfterReturning 추가

@AfterReturning(value = "@annotation(LogReturn)", returning ="returnValue")
public void logResults(JoinPoint joinPoint, Object returnValue){
	MethodSignature  methodSignature =(MethodSignature) joinPoint.getSignature();
    logger.trace("method name :[{}]", methodSignature.getName()); 
    logger.trace("return type:[{}]",methodSignature.getReturnType()); 
    
    logger.trace("return value:[{}]", returnValue);
더보기

1) @AfterReturning

- 반환값이 존재하는 함수에 대해 pointcut 지정해 줄 수 있음. (void여도 상관은 없음)

- 반환값 받아오기 위해 사용

 

더보기

2) returning ="returnValue"

- 반환하는 값을  함수에 autowired시켜야됨 -> 어떤 인자에 autowired 시켜줄지를 위해 returning 들어감

- aspectj가 returnValue이름을 가진 인자에  param값을 대입해줌

 

 

2. PostController

return 값이 존재하는 함수 찾아 @LogReturn 붙이기

 


마무리

정확하게 쓰기 위해선 알아야 할 게 많음

영 이해가 안되면 반드시 이해할 필요는 없음. 

'SpringBoot' 카테고리의 다른 글

Interceptors & Filters  (0) 2022.03.24
Exception Handling - 강의 정리  (0) 2022.03.24
Validation - 강의정리  (0) 2022.03.22
Logging -강의  (0) 2022.03.22
Spring Boot Properties - 강의 정리  (0) 2022.03.22