티스토리 뷰
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
링크
TAG
- JAVA 프로그래밍
- 언리얼엔진
- 자바
- 일급 객체
- Java
- Stack Area
- 언리얼엔진5
- 도커
- RESTfull
- 코프링
- JVM
- vite
- react.js
- First-class citizen
- 스브링부트
- ai통합
- springai
- 카프카 개념
- method Area
- 타입 안전성
- redis
- cqrs
- model context protocol
- MCP
- generated_body()
- 디자인패턴
- Heap Area
- 코틀린
- unreal engjin
- 스프링부트
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
글 보관함