首页 > 解决方案 > 在非托管 @Bean 上使用 @ConfigurationProperties

问题描述

我想从@ConfigurationProperties出色的设施中受益,而无需将 bean 暴露在我的上下文中。这不是@Primaries诸如此类的问题,我根本无法将另一个暴露Datasource在上下文中。我怎样才能实现以下目标?

@ConfigurationProperties("com.non.exposed.datasource.hikari")
public DataSource privateHikariDatasource() {
    if (Objects.isNull(this.nonExposedDatasource)) {
        this.nonExposedDatasource = this.nonExposedDatasourceProperties.initializeDataSourceBuilder().build();
    }
    return this.nonExposedDatasource;
}

感谢@LppEdd的回答,最终完美的解决方案是:

@Autowired
private Environment environment;

public DataSource privateHikariDatasource() {
    if (Objects.isNull(this.nonExposedDatasource)) {
        this.nonExposedDatasource = bindHikariProperties(this.nonExposedDatasourceProperties.initializeDataSourceBuilder().build());
    }
    return this.nonExposedDatasource;
}

//This does exactly the same as @ConfigurationProperties("com.non.exposed.hikari") but without requiring the exposure of the Datasource in the ctx as @Bean
private <T extends DataSource> T bindHikariProperties(final T instance) {
    return Binder.get(this.environment).bind("com.non.exposed.datasource.hikari", Bindable.ofInstance(instance)).get();
}

然后您可以在内部调用您的 beanthis.privateHikariDatasource()以供其他 bean 使用。非常感谢@LppEdd!

标签: javaspringspring-boot

解决方案


由于这DataSource是类私有的,并且包含类可以/在 Spring 上下文中,因此您可以拥有一个@ConfigurationProperties

@ConfigurationProperties("com.foo.bar.datasource.hikari")
public class HikariConfiguration { ... }

哪个,通过注册它@EnableConfigurationProperties,可用于自动装配

@EnableConfigurationProperties(HikariConfiguration.class)
@SpringBootApplication
public class Application { ... }

因此可以在包含类中自动装配

@Component
class MyClass {
   private final HikariConfiguration hikariConfiguration;  
   private DataSource springDatasource;

   MyClass(final HikariConfiguration hikariConfiguration) {
      this.hikariConfiguration = hikariConfiguration;
   }

   ...

   private DataSource privateSingletonDataSource() {
      if (Objects.isNull(this.springDatasource)) {
         this.springDatasource = buildDataSource(this.hikariConfiguration);
      }

      return this.springDatasource;
   }
}

buildDataSource手动构建DataSource实例。
请记住,在构建DataSource.


最后的回应是你不能重复使用DataSourceProperties. 你甚至不能扩展它来改变属性的前缀。上下文中只能存在它的一个实例。
你能做的最好的就是模仿 Spring 所做的事情。

com.non.exposed.datasource.hikari.url=testUrl
com.non.exposed.datasource.hikari.username=testUsername
com.non.exposed.datasource.hikari.password=testPassword
...

您可以定义一个新@ConfigurationProperties

@ConfigurationProperties("com.non.exposed.datasource")
public class NonExposedProperties {
    private final Map<String, String> hikari = new HashMap<>(8);

    public Map<String, String> getHikari() {
        return hikari;
    }
}

@Configuration然后,在你的/类中自动装配这个属性@Component类。
遵循代码内注释。

@Configuration
public class CustomConfiguration {
    private final NonExposedProperties nonExposedProperties;
    private DataSource dataSource;

    CustomConfiguration(final NonExposedProperties nonExposedProperties) {
        this.nonExposedProperties= nonExposedProperties;
    }

    public DataSource dataSource() {
        if (Objects.isNull(dataSource)) {
            // Create a standalone instance of DataSourceProperties
            final DataSourceProperties dataSourceProperties = new DataSourceProperties();

            // Use the NonExposedProperties "hikari" Map as properties' source. It will be
            // {
            //    url      -> testUrl
            //    username -> testUsername
            //    password -> testPassword
            //    ... other properties
            // }
            final ConfigurationPropertySource source = new MapConfigurationPropertySource(nonExposedProperties.getHikari());

            // Bind those properties to the DataSourceProperties instance
            final BindResult<DataSourceProperties> binded =
                    new Binder(source).bind(
                            ConfigurationPropertyName.EMPTY,
                            Bindable.ofInstance(dataSourceProperties)
                    );

            // Retrieve the binded instance (it's not a new one, it's the same as before)
            dataSource = binded.get().initializeDataSourceBuilder().build();
        }

        // Return the constructed HikariDataSource
        return dataSource;
    }
}

推荐阅读