AOP 구현 방식 2가지
Spring은 AOP(관심사 분리 — 로깅·트랜잭션·보안 등 부가기능을 핵심 로직과 분리)를 프록시 객체로 구현한다. 대상 빈을 감싸는 프록시를 런타임에 생성하는 방법이 두 가지다.
1) JDK Dynamic Proxy
- 조건: 인터페이스가 반드시 있어야 함
- 원리:
java.lang.reflect.Proxy+InvocationHandler로, 인터페이스를 구현하는 프록시 클래스를 런타임에 생성 - 한계: 인터페이스에 선언된 메서드만 프록시 가능 (구현체의 인터페이스 외 메서드는 프록시 안 됨)
- 성능: 생성되는 바이트코드가 단순해서 CGLIB보다 빠름
2) CGLIB
- 조건: 인터페이스 불필요 — 구체 클래스를 상속해서 프록시 생성
- 원리: ASM 바이트코드 조작 라이브러리로 대상 클래스의 서브클래스를 런타임 생성
- 한계:
final클래스/메서드는 상속 불가라 프록시 불가,static메서드도 프록시 불가 - 성능: 바이트코드가 더 복잡해서 JDK Proxy보다 약간 느림 (단, 근래 버전은 격차가 많이 줄었음)
Spring/Spring Boot의 선택 기준 (헷갈리기 쉬운 지점)
- Spring 프레임워크 자체의 기본 로직: 대상 빈이 인터페이스를 구현하면 JDK Proxy, 안 하면 CGLIB (
proxy-target-class=false가 기본값) - Spring Boot 2.0부터: 이 기본값을 아예 뒤집어서
spring.aop.proxy-target-class=true를 디폴트로 설정 → 인터페이스가 있어도 CGLIB을 기본으로 사용- "인터페이스가 있으면 무조건 JDK Proxy"라고 착각하기 쉬운데, Spring Boot 프로젝트라면 실제로는 CGLIB이 기본값이라는 게 헷갈리는 핵심 포인트
spring.aop.proxy-target-class=false로 명시하면 JDK Proxy로 되돌릴 수 있음 (단 인터페이스 필수)
연관 함정: Self-invocation
CGLIB이든 JDK Proxy든, AOP는 프록시 객체를 통해 호출될 때만 적용된다. 같은 클래스 안에서 this.method()로 내부 호출하면 프록시를 거치지 않아 @Transactional 등이 씹힌다.
트랜잭션·보안·로깅에서의 활용
@Transactional,@Secured/@PreAuthorize,@Async등이 전부 이 프록시 메커니즘 위에서 동작 — 어노테이션이 붙은 메서드 호출 전후로 프록시가 부가 로직(트랜잭션 시작/커밋, 권한 체크, 비동기 위임 등)을 끼워 넣는 구조
반응형
'Framework > Spring' 카테고리의 다른 글
| Spring @Transactional이 동작하지 않는 이유: Self-Invocation 정리 (0) | 2025.05.07 |
|---|---|
| [Batch] 배치 애플리케이션의 이해와 Spring Batch 기본 활용법 (0) | 2025.01.19 |
| [Batch] Spring Batch Scope의 개념과 Job Parameter 사용 방법 (0) | 2024.11.27 |
| [Spring] Spring Boot 환경설정 가이드 (2) | 2024.09.23 |
| [Spring] Spring 프레임워크란 무엇인가? (1) | 2024.09.20 |
댓글