一尘不染

在非托管@Bean上使用@ConfigurationProperties

spring-boot

我想从@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();
}

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


阅读 294

收藏
2020-05-30

共1个答案

一尘不染

由于这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;
    }
}
2020-05-30