티스토리 뷰

728x90

 

1. 왜 어노테이션 + Reflections 인가?

기능 기존 코드 어노테이션+Reflections

플러그인/핸들러 등록 ServiceLoader, XML, 수동 등록 어노테이션 태깅 후 자동 스캔
읽기 쉬움 O O+ 메타데이터가 코드 옆에 위치
유지보수성 핸들러 추가 시 설정 수정 필요 Zero‑Config: 클래스만 추가하면 끝

어노테이션은 메타데이터를, Reflections는 클래스패스 스캐닝을 담당해 런타임 자동 구동 시나리오를 간결하게 만들어 줍니다.


2. 프로젝트 설정

build.gradle.kts

plugins {
    java
}

dependencies {
    implementation("org.reflections:reflections:0.10.2")
    // 필요 시 로깅
    implementation("org.slf4j:slf4j-simple:2.0.13")
}

Gradle 6 이상이면 BOM 없이 버전만 맞춰 주면 됩니다.


3. 커스텀 어노테이션 정의하기

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface AutoRegister {
    String value() default ""; // 선택적 식별자
}
  • @Retention(RUNTIME): 런타임에서도 어노테이션 정보 유지 → Reflections가 읽을 수 있음
  • @Target(TYPE): 클래스, 인터페이스, enum 등에 부착

4. Reflections 구조 한눈에 보기

Reflections (루트)
 ├─ Scanner           : 무엇을 찾을지 (TypesAnnotatedScanner 등)
 ├─ Store             : 스캔 결과 캐싱
 └─ ConfigurationBuilder
      ├─ urls()       : 스캔할 패키지/모듈의 URL
      ├─ scanners()   : 사용할 스캐너 목록 지정
      └─ inputsFilter(): 포함/제외 패턴

핵심은 ConfigurationBuilder로 스캐너와 경로를 선언적으로 묶는 것.


5. 어노테이션 스캔 실습

Reflections reflections = new Reflections(
    new ConfigurationBuilder()
        .forPackage("com.example") // 루트 패키지
        .addScanners(new TypeAnnotationsScanner())
);

// @AutoRegister가 붙은 모든 타입 가져오기
Set<Class<?>> registrables = reflections.getTypesAnnotatedWith(AutoRegister.class);
registrables.forEach(System.out::println);

TypeAnnotationsScanner는 클래스/인터페이스에 달린 어노테이션을 찾아 Store에 기록합니다.

필터링 팁

.reflections"
    .forPackage("com.example", ClassLoader.getSystemClassLoader())
    .filterInputsBy(s -> s.endsWith("Handler.class"))

JAR 파일이 많은 대규모 프로젝트라면 필터링 으로 스캔 범위를 좁혀 성능을 개선하세요.


6. 실전 예제 — 자동 의존성 등록

6‑1. 핸들러 인터페이스

public interface EventHandler {
    void handle(Event e);
}

6‑2. 구현체에 태그 달기

@AutoRegister("user")
public class UserCreatedHandler implements EventHandler { … }

6‑3. 부트스트랩 코드

class EventDispatcher {
    private final Map<String, EventHandler> handlers = new HashMap<>();

    EventDispatcher() {
        var reflections = new Reflections("com.example", new TypeAnnotationsScanner());
        for (Class<?> clazz : reflections.getTypesAnnotatedWith(AutoRegister.class)) {
            AutoRegister ar = clazz.getAnnotation(AutoRegister.class);
            handlers.put(ar.value(), (EventHandler) clazz.getDeclaredConstructor().newInstance());
        }
    }

    public void dispatch(String key, Event e) {
        handlers.getOrDefault(key, ev -> {}).handle(e);
    }
}

결과: 클래스를 추가하기만 하면 EventDispatcher가 자동으로 인스턴스를 등록합니다.


7. 고급 활용 & 성능 튜닝

기능 설명

메타‑어노테이션 @Documented, @Inherited를 활용해 DSL 느낌 살리기
스캐너 커스터마이징 MethodAnnotationsScanner, FieldAnnotationsScanner로 메소드/필드 대상 확장
캐시 재사용 Reflections 객체를 싱글턴으로 만들어 반복 스캔 방지
ParallelExecutor 대규모 검색 시 parallel stream으로 후처리 병렬화
728x90

'프로그래밍 > 자바' 카테고리의 다른 글

코틀린 코루틴 이해하기  (1) 2025.03.02
코틀린 입문 기초 정리  (2) 2025.02.27
자바 제네릭(Generic)  (0) 2024.09.30
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/05   »
1 2 3
4 5 6 7 8 9 10
11 12 13 14 15 16 17
18 19 20 21 22 23 24
25 26 27 28 29 30 31
글 보관함