Как уже упоминалось в Как получить valueOf & значения Enum и вызвать методы в интерфейсе, который он реализует, когда он определен как универсальный c параметр класса , я хочу преобразовать строки в Enums для набора перечислимых типов. Хотя ответ в этом посте решил мои проблемы с компиляцией, Spring не конвертирует (см. Обновление ниже). Вот информация из этого поста для справки:
Я использую интерфейс, чтобы отметить, какой набор перечислений для преобразования, и определить пару методов, используемых во время преобразования:
public interface SymbolEnum {
String getCode();
String getDescription();
}
Вот пример один из типов Enum, который я хочу получить из строки:
@RequiredArgsConstructor // using lombok
public enum Element implements SymbolEnum {
IRON("FE", "iron"),
HYDROGEN("H", "hydrogen"),
@Getter
private final String code;
@Getter
private final String description;
}
Если строка соответствует имени константы перечисления, свойству кода перечисления или свойству описания перечисления, то я хочу преобразовать в это перечисление , В противном случае код должен вызвать исключение IllegalArgumentException.
Я использую интерфейс Spring Converter для реализации моего конвертера:
@RequiredArgsConstructor
public class StringToSymbolEnum<T extends Enum<T> & SymbolEnum> implements Converter<String, T> {
private final T[] values;
@Override
public T convert(String source) {
try {
return Enum.valueOf(values[0].getDeclaringClass(), source);
} catch (IllegalArgumentException notEnumConstant) {
for (T value : values()) {
if (value.getDescription().equalsIgnoreCase(source)
|| value.getCode().equalsIgnoreCase(source)) {
return value;
}
}
throw notEnumConstant;
}
}
}
Здесь я зарегистрировал конвертер:
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new StringToSymbolEnum<Element>(Element.values()));
}
}
Но преобразователь никогда не вызывается. Как заставить Spring использовать конвертер?
Обновление
Я ожидаю, что конвертер будет вызываться при выполнении запроса HTTP GET к следующей конечной точке в контроллер:
@RestController
@RequiredArgsConstructor
@RequestMapping("/element")
public class ElementController {
@NonNull
private final ElementService elementService;
@GetMapping
public Element getElement(
@RequestParam(name="element")
Element element // During Spring's binding, I expect the registered converter to be called
) {
return elementService.getElement(element);
}
}
Обновление 2
Я нашел пару решений этой проблемы. Одним из решений является реализация Spring ConverterFactory
и регистрация его в FormatterRegistry
. Однако это решение представляется более подходящим для регистрации одного супертипа. В моем примере у меня есть классы, которые необходимо зарегистрировать, если они бывают двух типов, SymbolEnum
и Enum
. Вот моя попытка такого подхода:
public class StringToEnumConverterFactory <R extends Enum<R> & SymbolEnum>
implements ConverterFactory<String, R> {
@Override
public <T extends R> Converter<String, T> getConverter(Class<T> targetType) {
return new StringToSymbolEnumInner<T>(targetType.getEnumConstants());
}
private class StringToSymbolEnumInner <W extends R>
implements Converter<String, W> {
private final W[] values;
public StringToSymbolEnumInner(W[] values) {
this.values = values;
}
@SuppressWarnings("unchecked")
@Override
public W convert(String source) {
log.info("called converter with values ");
try {
return (W) Enum.valueOf(values[0].getDeclaringClass(), source);
} catch (IllegalArgumentException notEnumConstant) {
for (W value : values) {
if (value.getDescription().equalsIgnoreCase(source)
|| value.getCode().equalsIgnoreCase(source)) {
return value;
}
}
throw notEnumConstant;
}
}
}
Мне не нравится актерский состав, и он действительно не работает как фабрика, так как я должен зарегистрировать каждый SymbolEnum
класс.
Другой вариант заключается в использовании более явной регистрации следующим образом:
registry.addConverter(String.class,
Element.class,
new StringToSymbolEnum<Element>(
Element.values()));
Оба эти подхода работают. То есть конвертер вызывается. Есть ли лучший подход к этому? Почему моя первая попытка не работает? Мне интересно, имеет ли это отношение к стиранию типа.