일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- 테스트메소드
- foreignkey
- 서브쿼리
- 외부키
- appspec.yml
- 예약
- 참조키
- 적용우선순위
- MySQL
- 메소드명
- 메세지수정
- WeNews
- 네이티브쿼리
- Query
- application.yml
- 커밋메세지수정
- 2 > /dev/null
- querydsl
- 포트
- 추후정리
- 컨테이너실행
- 테스트
- appspec
- AuthenticationEntryPoint
- ㅔㄴ션
- ubuntu
- docker명령어
- 검색
- EC2
- subquery
- Today
- Total
제뉴어리의 모든것
AOP 복습 및 실제 구현 내용 본문
전체 항목
- AOP 핵심 개념
- 기본적인 사용 방법
- 실제 적용 사례
본 내용은 공부용이고 본인이 이해하기 위한 언어로 되어있습니다.
실제와는 차이가 있을 수 있습니다.
AOP 핵심 개념
아래는 AOP의 개념과
사용되는 기본용어이다.
아래의 코드와 보면서 이해하자.
여기서 계좌이체() , 대출승인() , 이자계산() 이란 핵심기능 메소드가 있는것이다.
그리고 그 핵심 기능마다 공통적으로 해주어야 하는 부가기능이 있다.
그것이 로깅, 보안. 트랜잭션이다.
그리고 계좌이체() , 대출승인() , 이자계산() 은 여러 메소드들 중에 Pointcut의 기준에 충족하여 AOP가 적용되는 것이다.
- Spring 에서 사용되는 AOP의 기본 코드 구조
@Aspect
@Component
public class PerformanceAspect {
private Logger logger = Logger.getLogger(getClass().getName());
@Pointcut("within(@org.springframework.stereotype.Repository *)")
public void repositoryClassMethods() {};
@Around("repositoryClassMethods()")
public Object measureMethodExecutionTime(ProceedingJoinPoint pjp) throws Throwable {
long start = System.nanoTime();
Object retval = pjp.proceed();
long end = System.nanoTime();
String methodName = pjp.getSignature().getName();
logger.info("Execution of " + methodName + " took " +
TimeUnit.NANOSECONDS.toMillis(end - start) + " ms");
return retval;
}
}
Advice
타겟 (특정 메소드) 에 적용 할 부가기능을 말한다.
위 코드에서 보자면
measureMethodExecutionTime () 메소드를 Advice라고 할 수 있겠다.
그리고 해당 메소드가 Advice임을 나타내는
Advice 애노테이션의 종류는
“around,” “before” and “after” 가 있다.
즉,
어드바이스는 부가기능의 구현 부분을 의미하고,
어드바이스임을 명시하고 작동 범위를 정의하는 어드바이스 어노테이션의 종류로는
“around,” “before” and “after” 가 있다.
Pointcut
어드 바이스를 적용할 타겟을 선정하는 기준이 되는 표현식이다.
위 코드에서 보자면
@Pointcut("within(@org.springframework.stereotype.Repository *)")
위 부분을 Pointcut이라고 할 수 있다.
쉽게 외우자면, (join) point 의 cut (line) 이라고 생각하면 된다.
그리고 포인트컷은 지시자와 함께 정의가 되는데, 지시자의 종류는 아래와 같다.
- execution : 메서드 실행 조인트 포인트를 매칭. 스프링 AOP에서 가장 많이 사용하며, 기능도 복잡하다.
- within : 특정 타입 내의 조인 포인트를 매칭한다.
- args : 인자가 주어진 타입의 인스턴스인 조인 포인트
- this : 스프링 빈 객체(스프링 AOP 프록시)를 대상으로 하는 조인 포인트
- target : Target 객체(스프링 AOP 프록시가 가르키는 실제 대상)를 대상으로 하는 조인 포인트
- @target : 실행 객체의 클래스에 주어진 타입의 애너테이션이 있는 조인 포인트
- @within 주어진 애너테이션이 있는 타입 내 조인 포인트
- @annotation 메서드가 주어니 애너테이션을 가지고 있는 조인 포인트를 매칭
- @args 전달된 실제 인수의 런타임 타입이 주어진 타입의 애너테이션을 갖는 조인 포인트
- bean 스프링 전용 포인트컷 지시자이고 빈의 이름으로 포인트컷을 지정한다.
그리고 정말 다양한 Poincut 표현식이 있다.
아래는 execution() 지시자를사용한 포인트컷의 기본적인 문법 구조이다.
execution([접근제한자 패턴] 타입패턴 [타입패턴.]이름패턴 (타입패턴 | "..", ...) [throws 예외 패턴])
Aspect
Advice와 Pointcut 이 합쳐진 것을 의미한다.
즉, 특정 메소드들에 적용할 부가기능과 해당 부가기능을 적용할 특정 메소드를 결정하는 기준까지
포함하는 개념이다.
Joinpoint
핵심기능 (메소드) 을 한 지점(호출지점) 으로써 표현되는 개념이다.
위 코드에서는 pjp.proceed(); 을 jointpoint 라고 할 수 있다.
말하자면 아래와 같은 개념이다.
Target
핵심 기능 메소드를 말한다.
즉, Advice가 적용되는 메소드이다.
Advisor
Advice + Pointcut 이 합쳐진 개념이다.
Spring AOP에서만 사용되는 특별한 용어이다.
위빙(Weaving)
위빙은 포인트컷에 의해서 결정된 타겟의 조인 포인트에 부가기능(어드바이스)를 삽입하는 과정을 뜻한다.
기본적인 사용 방법
의존성
implementation 'org.springframework.boot:spring-boot-starter-aop'
testCompileOnly 'org.projectlombok:lombok'
testAnnotationProcessor 'org.projectlombok:lombok'
@Before 를 사용한 기본 AOP 적용 방법
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;
@Aspect //Aspect임을 알리는 애노테이션
@Component //Aspect로써의 역할을 하기 위해선 빈으로 등록하여야 한다
public class TxAspect {
@Pointcut("execution(* kyh.template.aoptemplate.controller.MoneyController.*Money(..))")
private void lgg(){};
//Around가 아닌 Advice 애노테이션은 JoinPoint 만 인자로 받을 수 있다. ProceedingJoinPoint 불가
@Before("lgg()")
public void doBefore(JoinPoint joinPoint) {
System.out.println("로그 출력");
}
}
위에 코드에서 중요한 점은,
- Aspect는 Bean으로 등록하여야 한다
- @Around가 아닌 Advice 애노테이션은 JoinPoint 만 인자로 받을 수 있다. ProceedingJoinPoint 불가
실제 적용 사례
@Pointcut 을 이용하여 타겟의 기준을 만드는 것이 아니라.
특정 애노테이션이 붙은 메소드에서 Advice (부가기능) 를 적용 시킬 수 있다.
- 사용자정의 애노테이션
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface NeedMemberId {
}
- 애노테이션을 어드바이스의 적용 범위로 지정한 코드
package com.preproject.server.tx;
import com.preproject.server.member.wrapper.WrapperUserNamePasswordAuthenticationToken;
import lombok.RequiredArgsConstructor;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import org.aspectj.lang.reflect.MethodSignature;
@Aspect
@Component
@RequiredArgsConstructor
public class TxAspect {
private final PlatformTransactionManager transactionManager;
@Around("@annotation(com.preproject.server.tx.NeedMemberId)") //NeedMemberId를 붙인 메소드에
public Object applyTx(ProceedingJoinPoint joinPoint) throws Throwable {
TransactionStatus transaction = transactionManager.getTransaction(new DefaultTransactionDefinition());
try {
Object[] parameterValues = joinPoint.getArgs(); //우선 현재 Controller로 넘어 온 파라미터 값들을 가져 옴
Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); //SecurityContext에 저장된 Authentication 가져 옴
WrapperUserNamePasswordAuthenticationToken wUNPAT = (WrapperUserNamePasswordAuthenticationToken) authentication;
Long memberId = wUNPAT.getMemberId(); //SecurityContext에 저장된 서버가 인식하는 실제 MemberId
//조인 포인트 (호출 되는 메소드를 말한다) 가 호출되는 시점에 넘겨 받은 인자값들
MethodSignature signature = (MethodSignature) joinPoint.getSignature(); //메소드의 선언 부분에 대한 정보
Method method = signature.getMethod(); //메소드 자체의 대한 정보를 갖는 클래스
Parameter[] parameters = method.getParameters(); //메소드가 갖는 파라미터들의 정보
for (int i = 0; i < method.getParameters().length; i++) {
if (parameters[i].getName().equals("authMemberId")) { //i번째 파라미터의 이름이 "authMemberId"일 경우
parameterValues[i] = memberId; //해당 파라미터에 위에서 Authentication을 이용하여 얻은 "memberId"를 넣음
break;
}
}
Object object = joinPoint.proceed(parameterValues); //새롭게 설정된 파라미터 값들을 해당 메소드에 전달
transactionManager.commit(transaction);
return object;
} catch (RuntimeException runtimeException) {
transactionManager.rollback(transaction);
throw runtimeException;
}
}
}
위에 코드는 @NeedMemberId라는 애노테이션이 붙은 메소드에서
어드바이스 (applyTx() 메소드) 를 실행하겠다는 코드이다.
컨트롤러의 핸들러 메소드의 인자로, 서버에서 가지고 있는 사용자의
Authentication에서 얻은 memberId를 넣어주는 내용이다.
참조
https://shlee0882.tistory.com/206
https://dahye-jeong.gitbook.io/spring/spring/2020-04-09-aop-proxy
https://wwlee94.github.io/category/study/toby-spring/aop/basic-concept/
https://www.egovframe.go.kr/wiki/doku.php?id=egovframework:rte:fdl:aop:aspectj
'Spring Boot' 카테고리의 다른 글
application-dev.yml 에 여러 yml 파일 추가시키기 (0) | 2022.11.15 |
---|---|
application.yml 을 공통 내용과 환경별 파일로 나누기 (0) | 2022.11.04 |
ant pattern, * vs ** (0) | 2022.10.19 |
SMTP 를 이용하여 메일 보내기 (0) | 2022.10.18 |
Spring Boot 에서 ApplicationEvent 를 사용하여 비동기 처리하기 (0) | 2022.10.18 |