Aspectran 프레임워크는 자바의 표준 클래스 로딩 메커니즘을 넘어서는 독자적인 com.aspectran.core.context.loader.SiblingClassLoader
를 구현하여 사용합니다. 이는 단순한 클래스 로딩을 넘어, 동적 리로딩(Hot Reloading)과 모듈형 애플리케이션 구조와 같은 고급 기능을 제공하기 위해 특별히 설계되었습니다.
1. 표준 클래스 로더의 한계: 부모 우선 위임 모델
일반적인 Java 클래스 로더(예: URLClassLoader
)는 “부모 우선 위임 모델(Parent-First Delegation)”을 따릅니다. 이 모델의 동작 방식은 다음과 같습니다.
- 클래스 로딩 요청이 들어오면, 클래스 로더는 직접 클래스를 찾기 전에 먼저 부모 클래스 로더에게 요청을 위임합니다.
- 이 위임은 계층의 최상위인 부트스트랩 클래스 로더(Bootstrap ClassLoader)에 도달할 때까지 계속됩니다.
- 부모 클래스 로더가 클래스를 찾지 못했을 경우에만, 자식 클래스 로더가 비로소 자신의 경로에서 클래스를 찾기 시작합니다.
이 방식은 java.lang.Object
와 같은 핵심 클래스가 단 한 번만 로드되도록 보장하여 안정적이지만, 동적인 환경에서는 다음과 같은 명백한 한계를 가집니다.
- 클래스 교체 불가: 한번 부모 클래스 로더(예: 애플리케이션 클래스 로더)에 의해 클래스가 로드되면, 자식 클래스 로더는 설령 새로운 버전의 클래스 파일을 가지고 있더라도 절대 그 클래스를 다시 로드할 수 없습니다. 항상 부모가 로드한 버전이 우선권을 갖기 때문입니다. 이는 동적 리로딩(Hot Reloading)을 원천적으로 불가능하게 만듭니다.
2. Aspectran의 해결책: “형제 우선” 위임 모델
SiblingClassLoader
는 이 문제를 해결하기 위해 위임 모델을 뒤집어 “형제 우선(Sibling-First)” 또는 “자식 우선(Child-First)”에 가까운 전략을 채택합니다. loadClass()
메서드의 동작 방식은 다음과 같습니다.
- 자신과 형제들 먼저 탐색: 클래스 로딩 요청이 오면, 부모에게 위임하기 전에 자신과 자신에게 연결된 모든 “형제(Sibling)”
SiblingClassLoader
가 관리하는 리소스 경로에서 클래스를 먼저 찾습니다. - 최후의 수단으로 부모에게 위임: 자신과 형제들이 가진 리소스 내에서 클래스를 찾지 못했을 경우에만, 최상위
root
로더의 부모(일반적으로 애플리케이션의 기본 클래스 로더)에게 로딩을 위임합니다.
이러한 독자적인 접근 방식 덕분에, 각기 다른 디렉터리나 JAR 파일을 담당하는 여러 SiblingClassLoader
인스턴스들이 서로의 클래스를 공유하며 마치 하나의 거대한 가상 클래스 로더처럼 동작할 수 있습니다. 이는 모듈 간의 의존성 관리와 유연한 리소스 공유를 가능하게 합니다.
3. SiblingClassLoader의 주요 특징 및 메커니즘
가. 동적 핫 리로딩 (Dynamic Hot Reloading)
SiblingClassLoader
의 가장 강력한 기능은 reload()
메서드를 통해 JVM 재시작 없이 애플리케이션의 구성 요소나 클래스를 다시 로드할 수 있다는 점입니다.
- 내부 동작 원리:
reload()
가 호출되면,SiblingClassLoader
는 단순히 클래스를 다시 읽는 것이 아니라, 기존의SiblingClassLoader
인스턴스 그룹 전체를 폐기하고, 새로운SiblingClassLoader
인스턴스 그룹을 생성합니다. 이전 클래스를 로드했던 클래스 로더가 GC(Garbage Collection)의 대상이 되면서, 해당 클래스 로더에 의해 로드되었던 모든 클래스들도 함께 메모리에서 해제될 수 있게 됩니다. 그 후, 새로운 클래스 로더가 수정된 클래스 파일을 다시 로드하는 방식입니다. - 활용: 개발 중 코드 수정 후 서버 재시작 없이 변경 사항을 즉시 확인하거나, 운영 중인 서버에서 특정 모듈(플러그인)을 무중단으로 업데이트하는 데 사용됩니다.
나. 계층적 형제 구조 (Hierarchical Sibling Structure)
SiblingClassLoader
는 단일 구조가 아닌, root
로더와 그에 속한 여러 siblings
로 구성된 계층 구조를 가집니다. 이 구조를 통해 여러 개의 리소스 위치(예: 각기 다른 모듈의 classes
폴더나 jar
파일)를 하나의 논리적인 그룹으로 묶어 체계적으로 관리할 수 있습니다.
다. 선택적 클래스 제외 기능 (Exclusion)
excludePackage()
및 excludeClass()
메서드를 통해 특정 패키지나 클래스가 SiblingClassLoader
에 의해 로드되지 않도록 명시적으로 제외할 수 있습니다. 이는 클래스 로더 격리로 인해 발생할 수 있는 심각한 문제를 예방하는 매우 중요한 안전장치입니다.
- 문제 상황: 만약 여러 모듈이 공통으로 사용하는 라이브러리(예:
slf4j-api.jar
)가 제외되지 않는다면, 각 모듈의SiblingClassLoader
가 자신만의 버전의org.slf4j.Logger
클래스를 로드할 수 있습니다. 이 경우, 두 클래스는 이름은 같지만 서로 다른 클래스 로더에 의해 로드되었기 때문에 JVM에게는 완전히 다른 클래스로 인식됩니다. 결과적으로 한 모듈에서 생성한Logger
객체를 다른 모듈로 전달할 때ClassCastException
이 발생하게 됩니다. - 해결:
excludePackage("org.slf4j")
와 같이 공통 라이브러리를 제외하면, 해당 패키지에 대한 클래스 로딩 요청은 항상 부모 클래스 로더에게 위임됩니다. 이를 통해 애플리케이션 전체에서 해당 라이브러리의 클래스가 유일하게 존재함을 보장하여 클래스 충돌을 방지합니다.
4. 결론
SiblingClassLoader
는 Aspectran 프레임워크의 유연성과 확장성을 뒷받침하는 핵심 기술입니다. 표준 클래스 로딩 모델의 한계를 극복하고 모듈성, 동적 업데이트, 개발 편의성을 제공함으로써, 복잡하고 현대적인 애플리케이션 아키텍처를 효과적으로 지원합니다.