본문 바로가기
Spring

[Spring] @Around

by moca7 2024. 10. 23.

 

 

 

ㅁ AfterLoggingAdvice는 잠시 끈다. @Aspect를 주석처리한다.

- 빈으로도 등록하고 AOP로도 등록해야 한다. 

- @Aspect가 AOP로 등록하는 어노테이션이다.

- 빈 등록은 3가지 방법이 있는데 자바 방식으로 SpringBeanConfig 클래스를 만들었었다.

 

 

 

 

ㅁ com.br.sbatis.aop에 AroundLoggingAdvice 클래스를 만든다.

 

 
package com.br.sbatis.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Aspect
public class AroundLoggingAdvice {

 
  /*
   * Around Advice 메소드 작성법
   * 1. 반환타입 : Object
   * 2. 메소드명 : 마음대로
   * 3. 매개변수 : ProceedingJoinPoint 타입 객체 (생략 불가능)
   */
 
  @Around("execution (* com.br.sbatis.controller.*Controller.*(..) )")
  public Object aroundLoggingAdvice(ProceedingJoinPoint prJoinPoint) throws Throwable {
   
    // Pointcut 동작이전 시점 (@Before)
    log.debug("controller메소드 동작 이전에 실행됨");
   
    Object obj = prJoinPoint.proceed(); // 핵심로직 메소드를 동작 // 이 코드에 의해서 Pointcut(핵심로직)의 메소드가 실행된다. before after는 자동으로 실행되는데 around는 강제로 실행시켜줘야 한다.
   
    // Pointcut 동작이후 시점 (@After)
    log.debug("controller메소드 동작 이후에 실행됨");
   
    return obj;
   
  }
 
}

 

 

- 로그 출력을 위해 @Slf4j 어노테이션을 붙인다.

- AOP로 등록하기 위해 @Aspect 어노테이션을 붙인다.

 

 

- Around Advice는 반드시 반환타입을 Object로 해야 한다. Before, After Advice는 반환타입이 void 였다.

- 매개변수 ProceedingJoinPoint는 JoinPoint의 자식이다.

 

 

- 예외처리를 try~catch 구문으로 하지 않고 throws로 스프링이 알아서 하게 한다.

 

- Object obj = prJoinPoint.proceed();와 return obj;가 필수 구문이다. 그래서 매개변수 생략 불가.

 

 

 

 

 

- 예전에 마이바티스 프로젝트에서 서블릿 실행 이전에 동작되는 인코딩 필터 클래스를 만들었던 것과 조금 유사하다.

- chain.doFilter가 강제로 서블릿을 호출하는 구문이었다.

 

 

 

 

ㅁ SpringBeanConfig

 

 
package com.br.sbatis.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

import com.br.sbatis.aop.AfterLoggingAdvice;
import com.br.sbatis.aop.AroundLoggingAdvice;
import com.br.sbatis.aop.BeforeLoggingAdvice;

@EnableAspectJAutoProxy
@Configuration
public class SpringBeanConfig {

 
  @Bean
  public BeforeLoggingAdvice beforeLoggingAdvice() {
    return new BeforeLoggingAdvice();
  }
 
  @Bean
  public AfterLoggingAdvice afterLoggingAdvice() {
    return new AfterLoggingAdvice();
  }
 
  @Bean
  public AroundLoggingAdvice aroundLoggingAdvice() {
    return new AroundLoggingAdvice();
  }

}

 

- AroundLoggingAdvice에 @Aspect 어노테이션으로 AOP 등록했다고 끝이 아니고, 빈으로도 등록해줘야 한다.

 

 

 

 

 

ㅁ 서버 키고 메인 페이지 - 목록페이지 

 

 

 

- 위의 두줄은 메인페이지 요청시 출력되는 로그.

아래 3줄은 NoticeController의 noticeList() 메소드 실행 전후에 @Around 한줄씩 로그가 출력되었다.

컨트롤러의 저 메소드에도 로그 출력문이 한 줄 있었다.

 

 

 

===================================================================

 

 

 

ㅁ 

- 위에서는 제대로 동작되는지만 확인하려고 별 의미없는 로그를 출력해봤다.

- ProceedingJoinPoint 객체를 가지고도 요청시 전달값, ~, 어떤 요청 주소로 요청이 됐는지 등을 다 알아낼 수 있다.

 

 

 

 

ㅁ AroundLoggingAdvice

 

 
package com.br.sbatis.aop;

import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;

import javax.servlet.http.HttpServletRequest;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Aspect
public class AroundLoggingAdvice {

 
  /*
   * Around Advice 메소드 작성법
   * 1. 반환타입 : Object
   * 2. 메소드명 : 마음대로
   * 3. 매개변수 : ProceedingJoinPoint 타입 객체 (생략 불가능)
   */
 
  @Around("execution (* com.br.sbatis.controller.*Controller.*(..) )")
  public Object aroundLoggingAdvice(ProceedingJoinPoint prJoinPoint) throws Throwable {
   
    // Pointcut 동작이전 시점 (@Before)
    HttpServletRequest request
    = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest();
   
    // 요청시 전달값(파라미터)을 문자열로 만들어서 로그 출력
    Map<String,String[]> map = request.getParameterMap(); // ex) { no=["1"], name=["홍길동", "김말똥"] }
   
    String params = "";
    if(map.isEmpty()) {
      params += "No Parameter";
    }else {
      for(Entry<String, String[]> entry : map.entrySet()) {
        params += entry.getKey() + ":" + Arrays.toString(entry.getValue()) + " ";
      }
    }
   
   
   
    log.debug("{}", "-".repeat(50));
    log.debug("{} | {}", request.getMethod(), request.getRequestURI());
    log.debug("{}", params);

   
    Object obj = prJoinPoint.proceed(); // 핵심로직 메소드를 동작 // 이 코드에 의해서 Pointcut(핵심로직)의 메소드가 실행된다. before after는 자동으로 실행되는데 around는 강제로 실행시켜줘야 한다.
   
   
    // Pointcut 동작이후 시점 (@After)
    log.debug("{}\n", "-".repeat(50));
   
   
    return obj;
   
  }
 
}

 

 

- ProceedingJoinPoint 객체를 가지고 HttpServletRequest 객체를 알아낼 수 있다.

import하고 형변환 한다.

 

- RequestContextHolder.getRequestAttributes()는 스태틱 메소드여서 객체를 생성하지 않고 그냥 .으로 호출할 수 있다.

- getRequest()는 HttpServletRequest 객체를 반환하는 메소드다.

 

 

- request.getParameterMap()는 Map<String,String[]>을 반환한다.

- map에 요청시 전달값이 몇개가 담겼는지는 알 수 없다. 생성은 되었지만 비어있을 수도 있다.

- map.entrySet()는 Set<Entry<Map<String,STRING[]>>>을 반환한다.

 

 

 

 

 

 

 

ㅁ 서버키고 목록페이지 - 상세페이지 - 뒤로가기 - 등록페이지에서 제목과 내용입력하고 등록(자동으로 목록페이지)

 

 

 

 

 

 

ㅁ 지금은 로그출력하는데 AOP를 적용시키고 있는데, 트랜잭션 출력하는데도 AOP를 적용해본다.

- 하나의 서비스에 여러 dml문을 수행할 때가 있다. 그때는 우리가 별도로 트랜잭션 처리를 해야 한다.

 

'Spring' 카테고리의 다른 글

[Spring] 파일 업로드(1) 세팅  (1) 2024.10.23
[Spring] AOP 트랜잭션 처리  (0) 2024.10.23
[Spring] AOP(2) @Before Advice, @After Advice  (0) 2024.10.23
[Spring] AOP(1) 세팅  (0) 2024.10.22
[Spring] MyBatis(8) 게시글 일괄삭제  (0) 2024.10.22