Улучшение явности библиотеки Spring для расширяемых config-объектов - PullRequest
0 голосов
/ 01 февраля 2019

В настоящее время я работаю над библиотекой Spring, которая позволяет настраивать пользовательские конфигурационные классы (не имеющие отношения к @Configuration) из другой части приложения перед их использованием:

interface ConfigAdjuster<T extends Config<T>> {
    void adjust(T t);
}

abstract class Config<T extends Config<T>> {
     @Autowired
     Optional<ConfigAdjuster<T>> adjuster;

     @PostConstruct
     private void init() {
         //i know this cast is somewhat unsafe, just ignore it for this question
         adjuster.ifPresent(a -> a.adjust((T)this));
     }
}

Это можно использовать следующим образом:

class MyConfig extends Config<MyConfig> {
    //imagine many fields of more complex types
    public String myData;
}

@Configuration
class MyConfigDefaults {
    @Profile("dev")
    @Bean 
    public MyConfig devDefaults() {
        //imagine setting defaults values here
        return new MyConfig();
    }
}

Теперь пользователь библиотеки, использующий MyConfig, может где-то в своем приложении сделать следующее:

@Bean
public ConfigAdjuster<MyConfig> adjustDefaults() {
    return cfg -> {
        cfg.myData = "something_other_than_default";
    }
}

Самая большая проблема, которую я вижуПри таком подходе вся часть «настройка конфига» несколько скрыта для пользователя.Вы не можете легко сказать, что вы можете изменить конфигурацию по умолчанию с помощью ConfigAdjuster.В худшем случае пользователь пытается автоматически подключить объект конфигурации и пытается изменить его таким образом, что приводит к неопределенному поведению, поскольку другие компоненты уже могли быть инициализированы со значениями по умолчанию.

Существует ли простой способ сделать этоподходить более «говорящим», чем то, что есть сейчас?Вся идея состоит в том, чтобы не копировать и не вставлять целые части настройки по умолчанию config + в несколько проектов.

Один из способов сделать все это более явным - потребовать настройки в конструкторе Config, но это загрязняет всеконструктор и использование наследующих классов.

Есть какие-нибудь мысли по этому поводу?

Редактировать: Обратите внимание, что это упрощенная версия библиотеки, и я знаю о последствиях частного @PostConstructи т.д. Если у вас есть другой способ достичь всего этого без @PostConstruct, пожалуйста, поделитесь:)

Edit2: Позвольте мне еще раз обозначить основные цели этой библиотеки:

  1. Разрешитьопределение объектов конфигурации по умолчанию для пользователя-библиотеки
  2. Разрешить конечному пользователю (использующему зависимость с использованием этой библиотеки) перезаписать определенные части конфигурации по умолчанию перед ее использованием
  3. Сохранить библиотеку-пользователь из шаблона (например, определить 2. самостоятельно)

1 Ответ

0 голосов
/ 06 февраля 2019

Для вашей проблемы есть два решения:

1- определить универсальный настройщик примерно так:

public interface Customizer<T> {

    T customize(T t);

    boolean supports(Class target);
}

в вашей библиотеке есть конфигурация:

public class MyConfig {

    private String property;

    public MyConfig() {
    }

    public void setProperty(String property) {
        this.property = property;
    }
}

, поэтому ваша конфигурация по умолчанию должна выглядеть примерно так:

@Configuration
public class DefaultConfiguration {


    @Autowired(required = false)
    private List<Customizer> customizers;

    @Bean
    public MyConfig myConfig() {
        MyConfig myConfig = new MyConfig();
        myConfig.setProperty("default value");
        if (customizers != null) {     
          for (Customizer c : customizers) {
            if (c.supports(MyConfig.class)) {
                return (MyConfig) c.customize(myConfig);
            }
          }
        }
        return myConfig;
    }
}

, таким образом, единственное, что пользователь должен делать всякий раз, когда он хочет настроить ваш бин, - это реализовать Customizer, изатем объявите его как боб.

public class MyConfigCustomizer implements Customizer<MyConfig> {

    @Override
    public MyConfig customize(MyConfig myConfig) {
        //customization here
        return myConfig;
    }

    @Override
    public boolean supports(Class<?> target) {
        return MyConfig.class.isAssignableFrom(target);
    }
}

и он должен объявить это:

@Bean 
public Customizer<MyConfig> customizer(){
     return new MyConfigCustomizer ();
 }

Я думаю, что это отвечает на ваш вопрос, но это некрасиво (непроверенные предупреждения и список...) не самый лучший, так как все кажется пользователю настраиваемым, даже если это не .

2- Я предлагаю вам предоставить интерфейсы для Бинов, которые могут настраиваться пользователем, что-то вроде:

public interface MyConfigCustomizer{

MyConfig customize(MyConfig config);

}

ваша конфигурация по умолчанию:

@Configuration
public class DefaultConfiguration {


    @Autowired(required = false)
    private MyConfigCustomizer customizer;

    @Bean
    public MyConfig myConfig() {
        MyConfig myConfig = new MyConfig();
        myConfig.setProperty("default value");
        if (customizer != null) {
            return customizer.customize(myconfig);
        }
        return myConfig;
    }

}

таким образом, пользователь знает, что MyConfig можно настроить (и не всебобы).

...