【问题标题】:Is it possible to make Spring @Import or @Configuration parametrized?是否可以将 Spring @Import 或 @Configuration 参数化?
【发布时间】:2015-09-30 21:48:12
【问题描述】:

我创建了许多常见的小型 bean 定义容器 (@Configuration),我用它们来快速开发带有 Spring Boot 的应用程序,例如:

@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 ...

例如,这样的@Configurations 之一可以使用网络控制器为区域设置 cookie 设置会话存储以切换到选定的语言等。

使用和重用它们非常有趣,但是将其参数化真的很棒,这可以允许更多的重用。我的意思是:

伪代码

@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}");
    }

【问题讨论】:

    标签: java spring spring-mvc annotations spring-boot


    【解决方案1】:

    让我引用Spring Boot Reference Guide - Externalized Configuration

    “Spring Boot 允许您将配置外部化,以便您可以在不同的环境中使用相同的应用程序代码。”

    在我看来,自定义不是在导入时通过注释参数完成的,例如在您的第二个伪代码块中,而是在运行时进行自定义,例如在配置类中。让我调整你的第三个代码块(只有一个功能):

    @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@ConditionalOnExpressionso on。例如,使用@ConditionalOnClass,您可以检查特定类是否在您的类路径上,并为此类标识的库提供设置。使用@ConditionalOnMissingClass,您可以提供替代实现。在上面的示例中,我使用ConditionalOnMissingBeanFreeMarkerConfigurer 提供默认实现。此实现仅在没有可用的FreeMarkerConfigurer bean 时使用,因此可以轻松覆盖。

    看看 Spring Boot 或社区提供的 starters。 blog entry 也是一本好书。我从spring-boot-starter-batch-web 学到了很多东西,他们在德国 Java 杂志上有一个系列文章,但部分内容也在线,请参阅Boot your own infrastructure – Extending Spring Boot in five steps必读),尤其是“让您的启动器可配置”这一段通过使用属性”。

    【讨论】:

    • 你确定方法注解应该是@Configuration吗?另外,我非常不喜欢属性驱动的配置,因为我的大部分配置都不依赖于环境,而且这种驱动器将所有属性混合在一起。
    • @PiotrMüller 您可能应该在问题中提及您对属性的不喜欢,因为它会相当大地改变一个好的答案。在配置文件中获得更多结构的一种非常简单的方法是使用 YAML。如果你也不喜欢 YAML,那么你也应该提一下。
    • 1) 对于@Configuration 注释:我刚刚将您的代码用于“属性驱动的配置”。 2) 您甚至可以对不依赖于环境的配置数据使用属性。第二个代码块中的所有设置都非常适合在属性中进行配置。 3) 除了属性,还有很多其他的可能性。我用@ConditionalOnClass 的场景和博客条目的链接更新了我的答案。
    • @ChrLipp 我要求注释,因为我什至不知道方法级别是否允许@Configuration - 我的代码只是一个伪代码。 LocaleResolverConfigurator 是否将作为常规 @Configuration 类处理,并且所有包含 @Beans 的方法都将创建一个 bean?
    • 好的,我明白了。我确实错过了那个。我不认为这是可能的,我不太确定你为什么需要它。你有多个配置,你必须像以前一样导入它们——唯一的改变是你可以参数化配置。要摆脱导入,您还可以定义自己的 Enable* 注释,还可以将多个配置分组到单个 Enable* 注释中。
    【解决方案2】:

    虽然我喜欢将导入参数化的想法,但我认为现在使用 @Import@Configuration 并不合适。

    我可以想到两种使用动态配置的方法,它们不依赖于PropertySource 样式配置。

    1. 创建一个自定义 @ImportConfig 注释和注释处理器,该处理器接受硬编码到生成的源文件中的配置属性。
    2. 使用BeanFactoryPostProcessorBeanPostProcessor 分别添加或操作包含的bean。

    两者都不是特别简单的 IMO,但因为看起来你有一种特定的工作方式。因此,投入时间可能是值得的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2017-09-27
      • 2020-06-21
      • 1970-01-01
      • 1970-01-01
      • 2011-06-16
      • 2022-01-22
      • 2018-03-04
      • 1970-01-01
      相关资源
      最近更新 更多