Свойства конфигурации Spring — Полиморфные объекты в одном списке

#java #spring-boot #configurationproperties

Вопрос:

Я пытаюсь прочитать список элементов конфигурации из application.yaml компонента конфигурации через @ConfigurationProperties . Список может состоять из объектов с определенным базовым типом:

 @ConfigurationProperties("myapp")
public class MyConfiguration {
   private Set<Point> points;
}

public abstract class Point {
  private String name;
}

public class QuantityPoint extends Point {
  private Number value;
}

public class StatePoint extends Point {
  private String state;
}
 

Вот пример application.yaml , как я его себе представляю:

 myapp:
  points:
  - name: foo
    value: 5
  - name: bar
    state: bar
 

Я нашел несколько постов, которые, как я думал, решают именно этот вопрос, но до сих пор ничего не предлагалось. Я попытался поиграть с ConfigurationProperties("myapp.points") подтипами Point , но, как уже было сказано, ничего не сработало.

Честно говоря, мне интересно, как парсер должен уметь различать типы. Должно быть, не хватает чего-то вроде type аргумента, аналогичного тому, как мы преобразуем сущности в json с помощью Джексона.

На данный момент я получаю экземпляры Point (когда я удаляю abstract модификатор) и поля value и state теряюсь.

Редактировать

Я думаю, что добился некоторого прогресса в выяснении того, что нужно сделать. По-видимому, Spring использует SnakeYaml для анализа файла, и эта библиотека использует теги yaml для указания информации о типе, чтобы мой yaml выглядел примерно так

 myapp:
  points:
  - !!foo
    name: foo
    value: 5
  - !!bar
    name: bar
    state: bar
 

Есть несколько встроенных тегов, но мне нужно будет зарегистрировать свои собственные. Я нашел информацию о том, как написать пользовательский конструктор путем расширения AbstractConstruct , но я не смог найти ничего о том, как зарегистрировать это в экземпляре Yaml , настроенном springboot.

Я, по крайней мере, на правильном пути здесь?

Редактировать

Я заметил, что ConstructorException от snakeyaml приходит так рано в процессе запуска, что, вероятно, невозможно добавить мой SafeConstructor в процесс:

 @Component
public class FooConstructor extends SafeConstructor {

    public FooConstructor() {
        this.yamlConstructors.put(new Tag("!foo"), new ConstructFoo());
    }
    
    private class ConstructFoo extends AbstractConstruct {
        @Override
        public Object construct(Node node) {
            String val = (String) constructScalar((ScalarNode) node);
            System.out.println(val);
            return null;
        }
    }
}
 
 06:48:29.380 [restartedMain] ERROR org.springframework.boot.SpringApplication - Application run failed
org.yaml.snakeyaml.constructor.ConstructorException: could not determine a constructor for the tag !state
 in 'reader', line 5, column 5:
      - !foo
 

Комментарии:

1. baeldung.com/…

2. Пример в вашей ссылке преобразует строку «джон,2000» в объект сотрудника. Это не мой сценарий.

3. Ваш сценарий состоит в том, чтобы преобразовать a Map<String, Object> в a Point . Это не очень отличается

4. Хм, это хороший аргумент. Никогда не думал об этом с такой точки зрения. Я попробую, спасибо!

5. Это Converter<Map<String, Object>, Point> никогда не называется