ㅁ 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문을 수행할 때가 있다. 그때는 우리가 별도로 트랜잭션 처리를 해야 한다.