관점 지향 프로그래밍
AOP 도입 이유
| 의미 | 포인트컷 반환 타입
-----------|---------------------------------------------------|------------------------------------
1) @Before | 포인트컷 동작 이전에 수행(인터셉터와 동일한 시점) | void
2) @After | 포인트컷 동작 이후에 수행 | void
3) @Around | 포인트컷 동작 이전/이후에 모두 수행 | Object (포인트컷의 실행 결과를 반환)
표현식(Expression) 작성 방법
1) 형식
execution(반환타입 패키지.클래스.메소드(매개변수))
2) 의미
(1) 반환타입
① * : 모든 반환타입
② void : void 반환타입
③ !void : void를 제외한 반환타입
(2) 매개변수
① .. : 모든 매개변수
② * : 1개의 모든 매개변수
BeforeAop
package com.gdu.app10.aop;
import java.util.Arrays;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import lombok.extern.slf4j.Slf4j;
@Slf4j // private static final Logger log = LoggerFactory.getLogger(BeforeAop.class);
@Aspect
@Component
public class BeforeAop {
// 포인트컷 : 언제 동작하는가?
@Pointcut("execution(* com.gdu.app10.controller.*Controller.*(..))")
public void setPointCut() { } // 이름만 제공하는 메소드(이름은 마음대로 본문도 필요 없다.)
// 어드바이스 : 무슨 동작을 하는가?
@Before("setPointCut()")
public void beforeAdvice(JoinPoint joinPoint) {
/*
* Before 어드바이스
* 1. 반환타입 : void
* 2. 메소드명 : 마음대로
* 3. 매개변수 : JoinPoint
*/
/* ContactController의 모든 메소드가 동작하기 전에 요청(방식/주소/파라미터) 출력하기 */
// 1. HttpServletRequest
ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
HttpServletRequest request = servletRequestAttributes.getRequest();
// 2. 요청 파라미터 -> Map 변환
Map<String, String[]> map = request.getParameterMap();
// 3. 요청 파라미터 출력 형태 만들기
String params = "";
if(map.isEmpty()) {
params += "No Parameter";
} else {
for(Map.Entry<String, String[]> entry : map.entrySet()) {
params += entry.getKey() + ":" + Arrays.toString(entry.getValue()) + " ";
}
}
// 4. 로그 찍기 (치환 문자 {} 활용)
log.info("{} {}", request.getMethod(), request.getRequestURI()); // 방식, 주소
log.info("{}", params); // 요청 파라미터
}
}
AfterAop
package com.gdu.app10.aop;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.interceptor.TransactionInterceptor;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Aspect
@Component
public class AfterAop {
@Autowired
private TransactionInterceptor transactionInterceptor;
// 포인트컷 : 언제 동작하는가?
@Pointcut("execution(* com.gdu.app10.controller.*Controller.*(..))")
public void setPointCut() { }
// 어드바이스 : 무슨 동작을 하는가?
@After("setPointCut()")
public void afterAdvice(JoinPoint joinPoint) {
/*
* After 어드바이스
* 1. 반환타입 : void
* 2. 메소드명 : 마음대로
* 3. 매개변수 : JoinPoint
*/
// 로그 찍기
log.info("==================================================================");
}
}
AroundAop
package com.gdu.app10.aop;
import java.text.SimpleDateFormat;
import java.util.Date;
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.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Aspect
@Component
public class AroundAop {
// 포인트컷 : 언제 동작하는가?
@Pointcut("execution(* com.gdu.app10.controller.*Controller.*(..))")
public void setPointCut() {}
// 어드바이스 : 무슨 동작을 하는가?
@Around("setPointCut()")
public Object aroundAdvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
/*
* Around 어드바이스
* 1. 반환타입 : Object
* 2. 메소드명 : 마음대로
* 3. 매개변수 : ProceedingJoinPoint
*/
log.info("================================================================"); // 포인트컷 실행 이전에 실행할 코드(@Before 이전에 동작)
Object obj = proceedingJoinPoint.proceed(); // 포인트컷이 실행되는 시점
log.info("{}", new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date())); // 포인트컷 실행 이후에 실행(@After 이전에 동작)
return obj;
}
}
AppConfig
/* AOP를 이용한 트랜잭션 처리를 위해 필요한 Bean */
// TransactionInterceptor : 트랜잭션 처리를 위해 언제 rollback 할 것인지 정의하는 스프링 클래스
@Bean
public TransactionInterceptor transactionInterceptor() {
// 규칙
RuleBasedTransactionAttribute ruleBasedTransactionAttribute = new RuleBasedTransactionAttribute();
ruleBasedTransactionAttribute.setRollbackRules(Collections.singletonList(new RollbackRuleAttribute(Exception.class)));
MatchAlwaysTransactionAttributeSource matchAlwaysTransactionAttributeSource = new MatchAlwaysTransactionAttributeSource();
matchAlwaysTransactionAttributeSource.setTransactionAttribute(ruleBasedTransactionAttribute);
// 반환
return new TransactionInterceptor(transactionManager(), matchAlwaysTransactionAttributeSource);
}