AOP( Aspect-Oriented Programming)
객체지향 프로그래밍은 클래스가 단일 책임원칙을 가지도록 설계해 응집도를 높이고 결합도는 낮춘다.
하지만 클래스의 비즈니스 로직을 제외한 로깅, 트랜잭션과 같은 부가기능 여러 클래스에 중복적으로 필요한 기능이 존재한다. 이런 경우 AOP를 통해 코드의 중복성을 낮추고, 부가기능의 응집도를 높혀 관리할 수 있다.
AOP 적용할만한 것들
메소드 성능검사
DB와 엮인 대량의 데이터의 조회 쿼리 시간을 측정하는 등의 비즈니스 로직 호출의 앞뒤로 시간을 체크해 성능검사에 AOP를 적용할 수 있다.
트랜잭션 처리
성능검사와 마찬가지로 비즈니스 로직의 전후에 설정되는 번거로운 try catch 대신 AOP를 적용할 수 있다.
Spring AOP
Target
부가 관심사를 적용할 메인 관심 모듈(비즈니스 로직)
Advice
부가 관심 기능을 적용할 시점(메소드의 호출 전/후, 예외 발생 시)을 정의
태그 | 설명 |
@Before | 메소드 실행 전 실행 |
@AfterReturning | 메소드 정상 실행 후 실행 |
@AfterThrowing | 메소드가 예외를 던질 시 실행(catch) |
@After | 메소드가 정상, 예외 발생에 상관없이 실행(finally) |
@Around | 메소드의 실행 전, 후를 감싸 적용 |
JoinPoint
부가 관심 기능을 적용할 지점 (method, field) , Target 객체가 구현한 모든 메소드
PointCut
부가 관심 기능을 적용할 JoinPoint
Advice를 적용할 Target의 메소드 선별할 정규표현식
정리
동적 Proxy를 기반으로 AOP를 구현하므로 메소드 JointPoint만 지원, 프록시란?
AOP 구현
메소드의 성능검사를 위한 AOP 구현
Dependencies
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.27</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.19</version>
</dependency>
</dependencies>
AspectJ의 이점
- Spring AOP 보다 정교한 포인트컷 정의와 공통 관심사 구현 가능
- 컴파일 타임에 코드를 조작해 AOP 구현시 예외를 런타임이 아닌 컴파일 타임에 검출 가능
- 컴파일 타임의 바이트 코드 조작은 런타임 성능 오버헤드를 감소 시킬 수 있음
Target
@Component
public class StudentA {
private String subject = "수학";
public void takeClass() {
System.out.println(subject+" 수업 듣는 중");
}
}
Aspect
@Aspect
@Component // 스프링 컨테이너 등록 필요
public class AOP {
// 공통 관심사항 적용될 PointCut 정규표현식, test 패키지 내의 Student로 시작하는 클래스의
// takeClass 메소드
@Pointcut("execution(public * test.Student*.takeClass())")
public void profileTtarget() {}
// Adivce의 인자로 PointCut 필요, 성능검사를 위해 시작과 끝을 감싸는 @Around 선택
@Around("profileTtarget()")
public Object trace(ProceedingJoinPoint joinPoint) throws Throwable {
print("target : " + joinPoint.getSignature().toShortString());
// joinPoint의 클래스와 메소드 프린트
long start = System.currentTimeMillis();
// 타겟 메소드 실행
Object result = joinPoint.proceed();
long end = System.currentTimeMillis();
print("performance time : " + (end - start));
return result;
}
private void print(String string) {
System.out.println("======AOP======");
System.out.println(string);
System.out.println("===============");
}
}
Configuration
@ComponentScan("test")
@Configuration
@EnableAspectJAutoProxy // Aspect Component 스캔시 필요한 어노테이션
public class AppConfig {
}
Main
public class Main {
public static void main(String[] args) {
// ApplicationContext context = new ClassPathXmlApplicationContext("test/applicationContext.xml");
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
StudentA a = context.getBean(StudentA.class);
a.takeClass();
}
}
Console
알게 된 내용
- DispatcherServlet과 Handler 사이의 Interceptor와 예외를 모아 처리하는 ExceptionHandler는 관점을 분리하는 방법이지만 AOP는 특정 기능보다는 더 일반적인 적용범위를 가진다.
- Spring Transaction은 AOP가 맞다.