AOP 란?
AOP(Aspect Oriented Programming)는 관점지향적 프로그래밍의 약자로 스프링 기술의 핵심이 되는 개념입니다.
관점 지향은 어떤 로직(ex. 한 기능의 총 걸린 시간 측정하기)을 기준으로 각각 모듈화 하겠다는 것입니다.
만약 모든 기능의 시간(회원 가입시간, 조회 시간)을 측정해서 어디부분이 성능 저하를 일으키는지 구하고 싶으면 모든 메서드에 아래와 같은 로직을 적용 시켜줘야 합니다.
public ? 측정하려는_메서드(?){
Long start = System.currentTimeMillis();
try {
// 메서드의 본래 내용
} finally {
Long end = System.currentTimeMillis();
Long timems = end - start;
System.out.println("Task : "+joinPoint.toString() +" Time: "+timems+"ms");
}
}
메서드가 몇 안되면 상관없지만, 메서드 갯수가 수백개, 수천개가 넘어가면 일일히 이 코드를 적용 시켜주기 힘듭니다.
그래서 AOP에서 각 관점을 기준으로 모듈과 한다는것은 그 로직에 대한 코드를 모든 함수에 일일히 작성하지 않고, 모듈화(클래스 생성)해서 원하는함수들에게 지정을 시켜준다는 의미 입니다.
그래서 메서드의 부분들을 공통 관심 사항(cross-cutting concern)인지 핵심 관심 사항(core concern)인지 구분하고,
공통 관심 사항은 AOP를 적용해서 처리해주는 것입니다.
AOP 주요 개념
- Aspect : 위에서 설명한 흩어진 관심사를 모듈화 한 것으로, 공통 관심 사항을 모듈화 합니다.
- Target : Aspect를 적용하는 곳입니다. (클래스, 메서드 .. )
- Advice : 실질적으로 어떤 일을 해야할 지에 대한 것으로, 실질적인 부가기능을 담은 구현체입니다.
- JointPoint : Advice가 적용될 위치, 끼어들 수 있는 지점. 메서드 진입 지점, 생성자 호출 시점, 필드에서 값을 꺼내올 때 등 다양한 시점에 적용가능합니다. execute로 메서드 종료
- PointCut : JointPoint의 상세한 스펙을 정의한 것으로, 'A란 메서드의 진입 시점에 호출할 것'과 같이 더욱 구체적으로 Advice가 실행될 지점을 정할 수 있습니다.
AOP 적용 전 원래 코드
public Long join(Member member) {
long start = System.currentTimeMillis();
try {
validateDuplicateMember(member); //중복 회원 검증
memberRepository.save(member);
return member.getId();
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("join " + timeMs + "ms");
}
}
public List<Member> findMembers() {
long start = System.currentTimeMillis();
try {
return memberRepository.findAll();
} finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("findMembers " + timeMs + "ms");
}
}
AOP 예제 - 메서드 시간 특정하기
우선 aop 패키지를 하나 만들어주고, 안에 aop클래스를 추가했습니다.
@Aspect로 aop 파일인것을 명시하고, aop는 스프링 빈에 등록을 해야되기 때문에 @Component 어노테이션을 추가했습니다.
Aspect의 실행 시점은 아래와 같은 어노테이션으로 정의 할수 있습니다
- @Before (이전) : 어드바이스 타겟 메소드가 호출되기 전에 어드바이스 기능을 수행
- @After (이후) : 타겟 메소드의 결과에 관계없이(즉 성공, 예외 관계없이) 타겟 메소드가 완료 되면 어드바이스 기능을 수행
- @AfterReturning (정상적 반환 이후)타겟 메소드가 성공적으로 결과값을 반환 후에 어드바이스 기능을 수행
- @AfterThrowing (예외 발생 이후) : 타겟 메소드가 수행 중 예외를 던지게 되면 어드바이스 기능을 수행
- @Around (메소드 실행 전후) : 어드바이스가 타겟 메소드를 감싸서 타겟 메소드 호출전과 후에 어드바이스 기능을 수행
메서드 실행 시간이므로 @Around 어노테이션을 채택했고, 그의 인자로 적용할 메서드의 범위를 지정해 줬습니다.
execution(* example.boot..*(..)) 가 의미하는 내용은
리턴값이 *인 함수, example.boot 패키지 안에 있는 모든 파일의 모든 메서드에 aspect를 적용한 다는 의미입니다.
execution(* example.boot..*.service.(..)) 가 의미하는 내용은
리턴값이 *인 함수, example.boot 패키지 안에 있는 service 패키지의 모든 메서드에 aspect를 적용한 다는 의미입니다.
example.boot 부분에는 여러분의 프로젝트의 패키지를 작성해주시면 됩니다.
aop/TimeTraceAop.java
package example.boot.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class TimeTraceAop {
@Around("execution(* example.boot..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable{
Long start = System.currentTimeMillis();
try {
return joinPoint.proceed();
} finally {
Long end = System.currentTimeMillis();
Long timems = end - start;
System.out.println("Task : "+joinPoint.toString() +" Time: "+timems+"ms");
}
}
}
실행 결과 콘솔
별도 config 파일로 빈을 등록 했을때 순환 참조 오류
위의 예제는 @Componet 어노테이션으로 컴포넌트 스캔 방식으로 등록했지만, 만약에 별도 자바 파일로 아래와 같이 빈으로 등록했을땐, 모든 메서드에 대한 관점을 설정할때 아래의 함수도 순환참조 되는 오류가 생길수 있습니다.
SpringConfig.java
@Configuration
public class SpringConfig {
@Bean
public TimeTraceAop timeTraceAop() {
return new TimeTraceAop();
}
}
그러므로 아래와 같이 Configuration 파일을 제외 시켜서 순환 참조 오류를 해결 할 수 있습니다.
aop/TimeTraceAop.java
@Aspect
@Component
public class TimeTraceAop {
//아래와 같이 수정!!!!!
@Around("execution(* example.boot..*(..)) && !target(example.boot.SpringConfig)")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable{
Long start = System.currentTimeMillis();
try {
return joinPoint.proceed();
} finally {
Long end = System.currentTimeMillis();
Long timems = end - start;
System.out.println("Task : "+joinPoint.toString() +" Time: "+timems+"ms");
}
}
}
'Back-End > 🌱 Spring Boot (java)' 카테고리의 다른 글
[Spring JPA] 상속관계 엔티티 매핑 전략 (@Inheritance @DiscriminatorColumn) (0) | 2023.02.01 |
---|---|
[Spring JPA] 엔티티(연관 테이블) 생성하고 일대일,일대다 연관관계(1:1 1:n) 설정하기 (0) | 2023.01.31 |
[Spring 입문] 스프링부트 JPA를 이용해서 MySQL 연결하기 (AWS이용) 연동 (0) | 2023.01.21 |
[Spring 입문] 스프링부트와 MySQL연결하기 - JDBC Template (AWS이용) (0) | 2023.01.21 |
[Spring 입문] 스프링 빈(Bean)과 의존관계 설정(Dependency Injection) (0) | 2023.01.20 |
댓글