Spring настраиваемый конвертер для всех Enums

Я создал много классов Enum с методами int getID() и MyEnum withID(int), которые позволяют мне присваивать идентификатор значениям перечисления для целей сохранения (таким образом, избегая изменений из-за изменения порядка/имени в отношении внешнего хранилища перечисление).

Я хотел бы создать настраиваемый конвертер, который делает некоторые размышления, чтобы искать эти методы, и использовать их или создавать резервные копии для конверсий Ordinal/String, когда они не найдены.

Кажется ли универсальный конвертер Enum кому-либо? Это только мой второй набег на Конвертеры.

Ответ 1

Я бы сказал, что вы пытаетесь решить неправильную проблему. Я обычно сохраняю перечисления как строки и тем самым избегаю этой проблемы в первую очередь. Недостатком является, конечно, большее поле БД, но это вряд ли имеет значение.


Это сказало:

Я бы сказал, что это возможно вообще, но не в чистом виде. Что бы я сделал, это позволить всем этим перечислениям реализовать общий интерфейс (либо просто интерфейс маркера, либо тот, который содержит метод int getId()). Теперь зарегистрируйте свой PropertyEditor только для этого интерфейса, тогда вы не нарушаете слишком много стандартных функций.

Затем ваша следующая проблема заключается в том, что вы полагаетесь на статические методы factory, которые нельзя выполнить общим способом. Конечно, ваш PropertyEditor может сделать:

enumClass.getDeclaredMethod("withId", int.class).invoke(id)

но я бы назвал это очень хаки. Как насчет чего-то подобного:

Object target = null;
for(Object e : EnumSet.allOf(yourEnumClass)){
    if(e instanceof MyInterface && ((MyInterface)e).getId()==thisId){
        target = e;
        break;
    }
}
return target;

Теперь вы не используете какие-либо статические методы factory, и у вас есть безопасность времени компиляции, если ваши перечисления реализуют общий интерфейс.


Обновление: с новым SPI конвертера становится проще. Используйте пользовательский ConverterFactory:

public class CustomEnumConverterFactory implements
    ConverterFactory<String, Enum<?>>{

    @Override
    public <T extends Enum<?>> Converter<String, T> getConverter(
        final Class<T> targetType){
        return WithId.class.isAssignableFrom(targetType)
            ? new EnumWithIdConverter(targetType)
            : new StandardEnumConverter(targetType);
    }

}

Зарегистрируйте его следующим образом:

<bean id="conversionService"
    class="org.springframework.context.support.ConversionServiceFactoryBean">
    <property name="converters">
        <!-- converters is a set of both converters and converterfactories -->
        <bean class="foo.bar.CustomEnumConverterFactory" />
    </property>
</bean>

Ответ 2

Extended из WebMvcConfigurerAdapter

@Override
@SuppressWarnings("unchecked")
public void addFormatters(FormatterRegistry registry) {
    registry.addConverterFactory(new ConverterFactory<String, Enum>() {
        @Override
        public <T extends Enum> Converter<String, T> getConverter(Class<T> targetType) {
            return source -> {
                try {
                    return (T) Enum.valueOf(targetType, source);
                } catch (Exception e) {
                    return targetType.getEnumConstants()[Integer.parseInt(source)];
                }
            };
        }
    });
    super.addFormatters(registry);
}