Подтвердить что ты не робот

Можно ли параметризовать параметр Spring @Import или @Configuration?

Я создал множество обычных небольших контейнеров bean -definition (@Configuration), которые я использую для быстрого создания приложений с помощью Spring Boot like:

@Import({
   FreemarkerViewResolver.class, // registers freemarker that auto appends <#escape etc.
   ConfigurationFromPropertiesFile.class, // loads conf/configuration.properties
   UtfContentTypeResponse.class, // sets proper Content-language and Content-type
   LocaleResolverWithLanguageSwitchController // Locale resolver + switch controller
 );
 class MySpringBootApp ...

Например, один из таких @Configuration может настроить хранилище сеансов для куки файла locale с веб-контроллером для переключения на выбранный язык и т.д.

Они очень полезны для работы и повторного использования, но было бы здорово сделать его параметризованным, что могло бы позволить намного больше повторного использования. Я имею в виду что-то вроде:

Псевдокод:

@Imports( imports = {
  @FreemarkerViewResolver( escapeHtml = true, autoIncludeSpringMacros = true),
  @ConfigurationFromProperties( path = "conf/configuration.properties" ),
  @ContentTypeResponse( encoding = "UTF-8" ),
  @LocaleResolver( switchLocaleUrl = "/locale/{loc}", defaultLocale = "en"
})

Итак, я в основном имею в виду "настраиваемый @Configurations". Каким будет лучший способ сделать конфигурацию таким образом?

Может быть, что-то вроде этого (опять-таки, псевдо-код):

@Configuration
public class MyAppConfiguration {

    @Configuration
    public FreemarkerConfiguration freemarkerConfiguration() {
       return FreemarkerConfigurationBuilder.withEscpeAutoAppend();
    }

    @Configuration
    public ConfigurationFromPropertiesFile conf() {
       return ConfigurationFromPropertiesFile.fromPath("...");
    }

    @Configuration
    public LocaleResolverConfigurator loc() {
       return LocaleResolverConfigurator.trackedInCookie().withDefaultLocale("en").withSwitchUrl("/switchlocale/{loc}");
    }
4b9b3361

Ответ 1

Позвольте мне процитировать Spring Справочное руководство по загрузке - Внешняя настройка:

"Spring Загрузка позволяет экранжировать вашу конфигурацию, чтобы вы могли работать с одним и тем же кодом приложения в разных средах."

По моему мнению, настройка не выполняется во время импорта через параметры аннотации, как в вашем втором блоке псевдокода, вместо этого настройка выполняется во время выполнения, например. в классах конфигурации. Позвольте мне адаптировать ваш третий блок кода (только одна функция):

@Configuration
public class MyAppConfiguration {

    @Autowired
    private Environment env;

    // Provide a default implementation for FreeMarkerConfigurer only
    // if the user of our config doesn't define her own configurer.
    @Bean
    @ConditionalOnMissingBean(FreeMarkerConfigurer.class)
    public FreeMarkerConfigurer freemarkerConfig() {
        FreeMarkerConfigurer result = new FreeMarkerConfigurer();
        result.setTemplateLoaderPath("/WEB-INF/views/");
        return result;
    }

    ...

    @Bean
    public LocaleResolverConfigurator loc() {
        String defaultLocale = env.getProperty("my.app.config.defaultlocale", "en");
        String switchLocale = env.getProperty("my.app.config.switchlocale", "/switchlocale/{loc}");

        return LocaleResolverConfigurator.trackedInCookie().withDefaultLocale(defaultLocale).withSwitchUrl(switchLocale);
    }

Для LocaleResolverConfigurator конфигурация считывается из среды, определяются значения по умолчанию. Легко изменить значения по умолчанию, предоставив другое значение для параметра конфигурации любым из поддерживаемых способов (задокументированных в первой ссылке) - через командную строку или файл yaml. Преимущество над параметрами аннотации заключается в том, что вы можете изменить поведение во время выполнения, а не время компиляции.

Вы также можете ввести параметры конфигурации (если вы предпочитаете иметь их как переменную экземпляра) или использовать множество других условий, например. @ConditionalOnMissingBean, @ConditionalOnClass, @ConditionalOnExpression и так далее. Например, с помощью @ConditionalOnClass вы можете проверить, находится ли конкретный класс в вашем пути к классу и предоставить параметр для библиотеки, указанной этим классом. С помощью @ConditionalOnMissingClass вы можете предоставить альтернативную реализацию. В приведенном выше примере я использовал ConditionalOnMissingBean для реализации по умолчанию для FreeMarkerConfigurer. Эта реализация используется, когда нет FreeMarkerConfigurer bean, поэтому можно легко переопределить.

Взгляните на стартеры, предоставленные Spring Boot или сообществом. Хорошее чтение - это запись в блоге. Я многому научился у spring-boot-starter-batch-web, у них была серия статей в немецком журнале Java, но части также доступны в Интернете, см. Загрузите свою собственную инфраструктуру - Расширение Spring Загрузка в пять шагов (ДОЛЖНО ПРОЧИТАТЬ), и особенно пункт "Сделайте свой стартер настраиваемым, используя свойства".

Ответ 2

Хотя мне нравится идея параметрирования импорта, я думаю, что в настоящее время использование @Import и @Configuration не подходит.

Я могу думать о двух способах использования динамических конфигураций, которые не зависят от конфигурации стиля PropertySource.

  • Создайте собственный обработчик аннотации и аннотации @ImportConfig, который принимает свойства конфигурации, жестко закодированные в сгенерированные исходные файлы.
  • Используйте BeanFactoryPostProcessor или BeanPostProcessor для добавления или управления включенными вами beans соответственно.

Ни одна из них не является особенно простой ИМО, но поскольку похоже, что у вас есть особый способ работы. Таким образом, это может стоить потраченного времени.