Aspect-oriendted Programming (AOP)은 OOP를 보완하는 수단으로, 흩어진 Aspect를 모듈화 할 수 있는 프로그래밍 기법.
클래스들 안에 반복적으로 나타나는 비슷한 코드들
ex) 트랜잭션
AutoCommit false -> Query 실행 -> Commit or RollBack
ex) 성능관련 로깅
메소드 시작 지점 시간체크 -> 메소드 끝 지점 시간체크
다음과 같은 형태로 다른 클래스이지만 비슷한 기능들.
Advice가 적용되는 대상 객체 (클래스)
Pointcut의 상위 느낌으로 Target -> 클래스, PointCut -> 메소드
흩어진 관심사들을 모아서 모듈화 한것
Advice + PointCut
해야할 일 -> 실행하는 코드
Advice가 적용되는 대상 (메소드)
Advice가 실행되는 시점 -> 끼어드는 지점, 합류되는 지점
ex) 메소드 실행시점(가장 흔히 사용되는)
컴파일 타임에 적용하여 바이트 코드에 같이 적용
- 장점: 로드타임과 런타임때 성능 부하 없음
- 단점: 별도의 컴파일 과정이 필요
클래스르 로딩하는 시점 (컴파일은 순수히 클래스 파일만)
- 장점: 다양한 문법 사용가능
- 단점: 클래스 로딩할때 부하, 로드타임 위버 설정 필요(로드타임의 자바 에이전트 설정 필요)
스프링 AOP가 사용하는 방법, 프록시 기반
A라는 빈을 만들때, A를 감싸는 프록시 빈을 생성
- 장점: 아무런 설정 필요X, 문법이 쉬움
- 단점: 빈 생성시에 성능 부하(로드타임과 유사한 부하)
- 프록시 기반 aop
- 스프링 빈에만 적용
- 모든 AOP 기능을 뿐만이 아닌 스프링 IoC와 연동하여 엔터프라이즈 애플리케이션에서 가장 흔한 문제에 대한 해결책을 제공
프록시 패턴은 다음과 같은 형태로 클라이언트에게 실제 객체가 아닌 프록시 객체를 제공하는 패턴이다.
public interface Subject {
void doAction();
}
// 실제 객체
@Service
public class RealSubject implements Subject{
@SneakyThrows
@Override
public void doAction() {
Thread.sleep(1000);
System.out.println("do something...");
}
}
// 프록시 객체 (@Primary로 같은 타입의 빈이 있으면 해당 타입으로 주입)
@Primary
@Service
@RequiredArgsConstructor
public class Proxy implements Subject{
private final RealSubject realSubject;
@Override
public void doAction() {
long now = System.currentTimeMillis();
realSubject.doAction();
System.out.println("실행시간: " + (System.currentTimeMillis() - now));
}
}
이와 같은 형태로 실제 객체를 감싼 형태로 부가 기능을 추가하여 프록시 객체를 생성하고
@Component
@RequiredArgsConstructor
public class AppRunner implements ApplicationRunner {
// 프록시 객체가 주입됨
private final Subject subject;
@Override
public void run(ApplicationArguments args) {
subject.doAction();
}
}
실제로 실행을 하면 다음과 같이 부가 기능(시간 체크)이 정상적으로 작동한다.
이렇게 프록시 객체는 실제 객체를 감싸고 있는 형태로 객체를 제공함으로써 기존의 코드를 건드리지 않고 접근 제어 또는 부가기능이 추가 가능해진다.
- 매번 프록시 클래스를 작성해야한다.
- 여러 클래스 여러 메소드에 적용하면 결국 중복코드가 발생한다.
- 객체들의 관계가 복잡해지면?
- 그래서 등장한 것이 스프링 AOP
스프링 IoC 컨테이너가 제공하는 기반 시설과 동적 프록시를 사용하여 여러 복잡한 문제 해결
(어너테이션으로 간단하게 적용 가능, 적용법은 다음 기회에...)
동적으로(런타임에) 프록시 객체 생성하는 방법
- CGlib은 클래스 기반 프록시도 지원.
- 자바가 제공하는 방법은 인터페이스 기반 프록시 생성.
스프링 IoC: 기존 빈을 대체하는 동적 프록시 빈을 만들어 등록 시켜준다.
- 클라이언트 코드 변경 없음.
- AbstractAutoProxyCreator implements BeanPostProcessor -> 이 클래스가 프록시 빈을 생성해준다.