1. 왜 AOP가 필요한가?
AOP(Aspect Oriented Programming)는 관점 지향 프로그래밍이다. 공통관심사항과 핵심관심사항을 분리하는 것이다. 예를 들어 모든 메소드의 호출 시간을 측정하고자 할 때 수 천개의 메소드마다 일일히 try catch finally문 안에 System.currentTimeMillis();를 배치하는 수고스러움을 줄이고 싶을 때 AOP를 사용한다. 수 천개의 매소드에 코드는 모두 다르지만 공통 코드가 있기 때문에 AOP를 활용해 수고스러움을 덜 수 있다.
2. AOP 실습
1) @Aspect
해당 어노테이션을 써줘야 AOP를 사용할 수 있다.
기초 셋팅은 aop 패키지 > TimeTraceApp 클래스를 만들어 진행한다.
package com.hello.hellospring.aop;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class TimeTraceAop {
}
2) 메소드의 호출 시간 측정 코드 작성
이처럼 AOP를 활용해 메소드마다의 핵심 관심사항과 시간을 측정하는 공통 관심사항을 분리했다. 덕분에 핵심 관심 사항인 각 메서드에 부수적인 시간 추가 내용없이 깔끔하게 핵심 관심로직만 둘 수 있다.
package com.hello.hellospring.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class TimeTraceAop {
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable{
long start = System.currentTimeMillis();
System.out.println("START: "+joinPoint.toString());
try{
return joinPoint.proceed();
}finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("END : " + joinPoint.toString() + " " + timeMs + "ms");
}
}
}
3) 스프링빈 등록
간단하게 @Component로 컴포넌트 스캔으로 빈을 등록해도 된다.
package com.hello.hellospring.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class TimeTraceAop {
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable{
long start = System.currentTimeMillis();
System.out.println("START: "+joinPoint.toString());
try{
return joinPoint.proceed();
}finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("END : " + joinPoint.toString() + " " + timeMs + "ms");
}
}
}
하지만 Config 파일로 빈 등록하는 방법을 선호한다.
package com.hello.hellospring;
import com.hello.hellospring.aop.TimeTraceAop;
import com.hello.hellospring.repository.MemberRepository;
import com.hello.hellospring.service.MemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class SpringConfig {
private final MemberRepository memberRepository;
@Autowired
public SpringConfig(MemberRepository memberRepository){
this.memberRepository = memberRepository;
}
@Bean
public MemberService memberService(){
return new MemberService(memberRepository);
}
@Bean
public TimeTraceAop timeTraceAop(){ //여기
return new TimeTraceAop();
}
}
4) AOP 적용법
@Around 어노테이션을 사용해 원하는 범위에 AOP를 적용할 수 있다.
다음은 com.hello.hellospring에 있는 하위 패키지 메소드에 모두 적용한 모습이다.
package com.hello.hellospring.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("excution(* com.hello.hellospring..*(..))")
public Object execute(ProceedingJoinPoint joinPoint) throws Throwable{
long start = System.currentTimeMillis();
System.out.println("START: "+joinPoint.toString());
try{
return joinPoint.proceed();
}finally {
long finish = System.currentTimeMillis();
long timeMs = finish - start;
System.out.println("END : " + joinPoint.toString() + " " + timeMs + "ms");
}
}
}
어디서 병목이 있는지 한눈에 확인할 수 있도록 도와준다.
3. AOP 동작 방식
가장 먼저 컨트롤러가 실행되면 Service를 주입받는데, 이 때 Service는 프록시 기술로 대타를 세운 Service가 실행된다. 실제 Service는 실행되지 않는다. 이렇게 가짜 Service 안에서 실행되다가 joinPoint.proceed()가 실행되면 진짜 Service가 호출된다.
'BE > Spring' 카테고리의 다른 글
Mapped Statements collection does not contain value for 맵핑명 (0) | 2024.05.03 |
---|---|
Spring 변경사항 자동 반영 (0) | 2024.01.10 |
스프링 데이터 JPA 기초 지식 (0) | 2024.01.09 |
데이터베이스 스프링에 연결하기 (2. JPA 방식) (0) | 2024.01.05 |
데이터베이스 스프링에 연결하기 (1. 순수 JDBC 방식), DI와 OCP (0) | 2024.01.05 |