타입 컨버터가 왜 필요한가?
Camel 라우트에서 메시지 본문은 다양한 타입일 수 있습니다. 어떤 컴포넌트는 String을 기대하고, 다른 컴포넌트는 InputStream을 기대합니다. 타입 컨버터(Type Converter)는 이런 타입 불일치를 자동으로 해결해줍니다.
타입 컨버터 동작 원리
Camel은 시작 시 클래스패스를 스캔해 @Converter 어노테이션이 달린 메서드들을 TypeConverterRegistry에 등록합니다. 이후 getBody(TargetType.class)가 호출되면 적합한 컨버터를 찾아 자동 변환합니다.
내장 타입 컨버터 활용
// 자동 변환 - 내장 컨버터 사용
from("direct:test")
.process(e -> {
// String → Integer 자동 변환
Integer num = e.getIn().getBody(Integer.class);
// String → InputStream 자동 변환
InputStream is = e.getIn().getBody(InputStream.class);
// byte[] → String 자동 변환
String text = e.getIn().getBody(String.class);
});
// convertBodyTo() DSL
from("direct:input")
.convertBodyTo(String.class) // 본문을 String으로 변환
.convertBodyTo(String.class, "UTF-8") // 인코딩 지정
.log("${body}");
커스텀 타입 컨버터 작성
// 1. @Converter 클래스 정의
@Converter(generateBulkLoader = true) // 성능 최적화
public class OrderTypeConverter {
@Converter
public static Order fromString(String csv) {
String[] parts = csv.split(",");
return new Order(parts[0], parts[1], Double.parseDouble(parts[2]));
}
@Converter
public static String toString(Order order) {
return order.getId() + "," + order.getCustomer() + "," + order.getAmount();
}
@Converter
public static Order fromMap(Map<String, String> map) {
return new Order(map.get("id"), map.get("customer"),
Double.parseDouble(map.get("amount")));
}
}
// 2. TypeConverterLoader 파일 생성
// src/main/resources/META-INF/services/org/apache/camel/TypeConverterLoader
// 내용: com.example.OrderTypeConverter
@Converter 어노테이션 옵션
- @Converter: 기본 컨버터 메서드 표시 (static 또는 instance 메서드 모두 가능)
- @FallbackConverter: 매칭되는 컨버터가 없을 때 최후 수단으로 호출
@Converter
public class FallbackConverter {
@FallbackConverter
public static <T> T convertTo(Class<T> type, Exchange exchange, Object value,
TypeConverterRegistry registry) {
// 커스텀 폴백 로직
if (type == Order.class && value instanceof Map) {
return (T) new Order((Map) value);
}
return null;
}
}
TypeConverter 성능 최적화
// 벌크 로더 방식 (시작 시 빠른 등록)
@Converter(generateBulkLoader = true)
public class FastConverter {
@Converter
public static Order fromJson(String json, Exchange exchange) {
ObjectMapper mapper = exchange.getContext()
.getRegistry().lookupByNameAndType("objectMapper", ObjectMapper.class);
try {
return mapper.readValue(json, Order.class);
} catch (Exception e) {
return null;
}
}
}
TypeConverterRegistry 직접 접근
@Autowired
CamelContext camelContext;
void registerConverter() {
TypeConverterRegistry registry = camelContext.getTypeConverterRegistry();
registry.addTypeConverter(Order.class, String.class,
(type, exchange, value) -> Order.fromCsv((String) value));
}