在非托管@Bean上使用@ConfigurationProperties
我想从@ConfigurationProperties
出色的功能中受益,而无需将bean暴露在我的上下文中.这不是@Primaries
之类的问题,我根本无法将另一个Datasource
暴露给上下文.如何实现以下目标?
I would like to benefit from @ConfigurationProperties
fantastic facilities without needing to expose the bean into my context. It is not a problem of @Primaries
and the like, I simply cannot expose another Datasource
into the context. How can I achieve the following?
@ConfigurationProperties("com.non.exposed.datasource.hikari")
public DataSource privateHikariDatasource() {
if (Objects.isNull(this.nonExposedDatasource)) {
this.nonExposedDatasource = this.nonExposedDatasourceProperties.initializeDataSourceBuilder().build();
}
return this.nonExposedDatasource;
}
感谢@LppEdd的回答,最终的完美解决方案是:
Thanks to the answer by @LppEdd, the final perfect solution is:
@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();
}
然后,您可以使用this.privateHikariDatasource()
在内部调用您的bean,以供其他bean使用.
非常感谢@LppEdd!
Then you can call your bean internally with this.privateHikariDatasource()
to be used by your other beans.
Great thanks to @LppEdd!
由于此DataSource
是类的私有对象,并且包含类的DataSource
可以在Spring上下文中存在,也可以是,因此,可以有一个@ConfigurationProperties
类
Being that this DataSource
is private to a class, and that containing class can be/is inside the Spring context, you can have a @ConfigurationProperties
class
@ConfigurationProperties("com.foo.bar.datasource.hikari")
public class HikariConfiguration { ... }
通过@EnableConfigurationProperties
注册可以自动接线
@EnableConfigurationProperties(HikariConfiguration.class)
@SpringBootApplication
public class Application { ... }
因此可以在包含的类中自动接线
And thus can be autowired in the containing class
@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
时需要注意同步.
buildDataSource
will manually construct the DataSource
instance.
Remember that you need to take care of synchronization when building the DataSource
.
最终答复是您不能重复使用DataSourceProperties
.您甚至无法扩展它来更改属性的前缀.在上下文中只能存在一个实例.
您可以做的最好的事情就是模仿Spring的工作.
Final response is that you cannot re-use DataSourceProperties
. You can't even extend it to change the properties' prefix. Only a single instance of it can exist inside the context.
The best you can do is mimic what Spring does.
拥有
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
类中.
遵循代码中的注释.
Then, autowire this properties class in your @Configuration
/@Component
class.
Follow in-code comments.
@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;
}
}