Working with #Qualifier in Kotlin functions - spring

In Kotlin function parameters are always values which prevents #Qualifier() from being used at the parameter level.
If someone wanted to create multiple DataSources in a config class for different databases frequently accessed within the same application what is the recommended way to organize this?
Doing the following which seemed fairly common in Java isn't allowed in Kotlin.
#Configuration
class DatabaseAdminConfig {
#Bean
#ConfigurationProperties(prefix = "spring.ds_admin")
fun adminDataSource(): DataSource {
return DataSourceBuilder.create().build()
}
#Bean
fun adminJdbcTemplate(#Qualifier("adminDataSource") dsAdminDb: DataSource): JdbcTemplate {
return JdbcTemplate(dsAdminDb)
}
#ConfigurationProperties(prefix = "spring.ds_widget")
fun widgetDataSource(): DataSource {
return DataSourceBuilder.create().build()
}
#Bean
fun widgetJdbcTemplate(#Qualifier("widgetDataSource") widgetDataSource: DataSource): JdbcTemplate {
return JdbcTemplate(widgetDataSource)
}
}

There is no need to inject the dependency from the same class into the method that requires that dependency in Spring. You can just call the method directly.
#Configuration
class DatabaseAdminConfig {
#Bean
#ConfigurationProperties(prefix = "spring.ds_admin")
fun adminDataSource() = DataSourceBuilder.create().build()
#Bean
fun adminJdbcTemplate() = JdbcTemplate(adminDataSource())
#Bean
#ConfigurationProperties(prefix = "spring.ds_widget")
fun widgetDataSource() = DataSourceBuilder.create().build()
#Bean
fun widgetJdbcTemplate() = JdbcTemplate(widgetDataSource())
}
PS: function expression bodies (as above) make Spring Configuration classes look even more concise.
PPS: #Qualifier works totally fine for me. I just tested it.
PPPS: And for completeness to address the specific problem, as I commented: You must have accidentally imported the javax.inject.Qualifier annotation, which will not work. You need to import the org.springframework.beans.factory.annotation.Qualifier which works

Related

Switch bean by changing properties in Spring boot

I have one interface MyInterface, and 2 implementation beans: FirstImpl & SeconImpl. I want to switch between using these 2 implementations while program is running without restarting it, by only changing a property in application.properties file, e.g: interface.bean.default=FirstImpl change to interface.bean.default=SecondImpl.
Anyone knows how to do that with Spring boot?
You could try to use #ConditionalOnProperty:
#Configuration
public class MyInterfaceConfiguration {
#Bean
#ConditionalOnProperty(value = "my.interfacte.impl", havingValue="firstImpl")
public MyInterface firstImpl(){
return new FirstImpl();
}
#Bean
#ConditionalOnProperty(value = "my.interfacte.impl", havingValue="secondImpl")
public MyInterface secondImpl(){
return new SecondImpl();
}
}
and when you update your property in application.properties with actuator/refresh to:
my.interfacte.impl=firstImpl
you will have your FirstImpl instance. When you have:
my.interfacte.impl=secondImpl
you will have your SecondImpl.
#Hasan, your update only works if I customize it a little bit as below:
#Configuration
#RefreshScope
public class MyInterfaceConfiguration {
#Value("${my.interfacte.impl}")
String impl;
#Bean
#RefreshScope
public MyInterface getBean(){
if ("firstImpl".equals(impl)) {
return new FirstImpl();
} else if ("secondImpl".equals(impl)) {
return new SecondImpl();
}
return null;
}
}
I have to use 2 #RefreshScope at class level and bean creation method level!

#Bean method, allthough in different classes, clashes Beans

Suppose I have two config classes that are the same, expect from a #Value tag, which is mapped to the corresponding property. As the #Bean annotated methods of this classes are equally named, I'm getting clashes. It doesn't seem to work neither using #Scope("prototype") nor calling the bean() method using "this".
I need all of the methods to be #Beans, so they can be managed by the spring context.
As both methods need to be configured with ClassA and ClassB properties, inheritance is not a good approach neitherdynamically injecting beans to context using a factory / register.
I know how to solve this changing bean() name to beanA() and beanB() but it has to exists a cleaner solution.
I need a cleaner solution to avoid code repetition. Nowadays if a new setter is added to ComplexObject, I have to modify classA and ClassB. Imagine having 100 Classes..that would be a lot of work.
#Configuration
public class ClassA{
....
#Value("${some-config.important-property}")
private String importantPropertyOnlyExistingInThisClass;
#Bean
#Scope("prototype")
public ComplexObject bean(){
ComplexObject o = new ComplexObject();
o.setImportantProp(this.importantPropertyOnlyExistingInThisClass)
return o;
}
#Bean
#Scope("prototype")
public AnotherComplexObject beanUsingBean(){
AnotherComplexObject aO = new AnotherComplexObject();
ao.setComplexObject(bean())
return aO;
}
}
#Configuration
public class ClassB{
....
#Value("${another-config.important-property}")
private String importantPropertyOnlyExistingInThisClass;
#Bean
#Scope("prototype")
public ComplexObject bean(){
ComplexObject o = new ComplexObject();
o.setImportantProp(this.importantPropertyOnlyExistingInThisClass)
return o;
}
#Bean
#Scope("prototype")
public AnotherComplexObject beanUsingBean(){
AnotherComplexObject aO = new AnotherComplexObject();
ao.setComplexObject(bean())
return aO;
}
}
I would expect to have an easier way to initialize similar beans using java config.

How inject DataSource(HikariCP) in Auto Configuration class?

I'm trying to use Spring Boot 2 + Spring Data + Custom Auto Configuration Classes but for some reason can't inject DataSource(provided by HikariCP) in the third class.
#Configuration
#AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
public class InitialAutoConfiguration {
//Beans to load in theory first.
}
#Configuration
#AutoConfigureOrder(Ordered.LOWEST_PRECEDENCE)
#AutoConfigureAfter(InitialAutoConfiguration.class)
#EntityScan(basePackageClasses = Asset.class)
#EnableJpaRepositories(basePackageClasses = AssetRepository.class,
repositoryBaseClass = BaseRepositoryImpl.class,
repositoryFactoryBeanClass = ExtendedJpaRepositoryFactoryBean.class)
public class JpaAutoConfiguration { //Load Jpa Classes
}
#Configuration
#AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
#AutoConfigureAfter(JpaAutoConfiguration.class)
#ComponentScan(basePackages = {"com.xxx"})
public class ServiceConfiguration {
#Inject
private DataSource dataSource; //Datasource is null
#Bean
public DbPropertySourcesPlaceholderConfigurer dbPropertySourcesPlaceholderConfigurer() {
DbPropertySourcesPlaceholderConfigurer placeholderConfigurer = new DbPropertySourcesPlaceholderConfigurer(dataSource);
placeholderConfigurer.setPlaceholderPrefix("%{");
placeholderConfigurer.setPlaceholderSuffix("}");
return placeholderConfigurer;
}
}
META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=com.xetec.autoconfigure.InitialAutoConfiguration,\
com.xetec.autoconfigure.JpaAutoConfiguration,\
com.xetec.autoconfigure.ServiceConfiguration
Looks like my classes are loading before the Spring Data Classes(DataSourceAutoConfiguration.Hikari).
Is there one way to first load the Spring Boot Starters Classes then after my custom ones please?
Thanks
I am no sure if the following change can solve your issue, but i meet with the similar problem
add static in your method
#Bean
public static DbPropertySourcesPlaceholderConfigurer dbPropertySourcesPlaceholderConfigurer() {
....
}
Your use of highest and lowest precedence is the wrong way round. Your ServiceConfiguration is ordered with highest precedence which means that it will be evaluated first.
Rather than using absolute ordering, I would use #AutoConfigureAfter(DataSourceAutoConfiguration.class)

Togglz with Spring #Configuration bean

I'm trying to implement Togglz & Spring using #Configuration beans rather than XML. I'm not sure how to configure the return type of the Configuration bean. For example:
#Configuration
public class SystemClockConfig {
#Bean
public SystemClock plainSystemClock() {
return new PlainSystemClock();
}
#Bean
public SystemClock awesomeSystemClock() {
return new AwesomeSystemClock();
}
#Bean
public FeatureProxyFactoryBean systemClock() {
FeatureProxyFactoryBean proxyFactoryBean = new FeatureProxyFactoryBean();
proxyFactoryBean.setActive(awesomeSystemClock());
proxyFactoryBean.setInactive(plainSystemClock());
proxyFactoryBean.setFeature(Features.AWESOME_SYSTEM_CLOCK.name());
proxyFactoryBean.setProxyType(SystemClock.class);
return proxyFactoryBean;
}
}
The systemClock method returns a FeatureProxyFactoryBean but the clients of this bean require a SystemClock. Of course, the compiler freaks over this.
I imagine it just works when XML config is used. How should I approach it when using a configuration bean?
I'm not an expert for the Java Config configuration style of Spring, but I guess your systemClock() method should return a proxy created with the FeatureProxyFactoryBean. Something like this:
#Bean
public SystemClock systemClock() {
FeatureProxyFactoryBean proxyFactoryBean = new FeatureProxyFactoryBean();
proxyFactoryBean.setActive(awesomeSystemClock());
proxyFactoryBean.setInactive(plainSystemClock());
proxyFactoryBean.setFeature(Features.AWESOME_SYSTEM_CLOCK.name());
proxyFactoryBean.setProxyType(SystemClock.class);
return (SystemClock) proxyFactoryBean.getObject();
}
But I'm not sure if this is the common way to use FactoryBeans with Spring Java Config.

Spring #Configuration bean created in #Bean method not enhanced by CGLIB

I'm trying to create a MainConfig that imports another Config by using an #Bean method instead of #Import like this :
#Configuration
public class MainConfig {
#Bean
public Service service() {
return new Service(infrastructureConfig().database());
}
#Bean
public OtherService otherService() {
return new OtherService(infrastructureConfig().database());
}
#Bean
public InfrastructureConfig intrastructureConfig() {
return new InfrastructureConfig();
}
}
#Configuration
public class InfrastructureConfig {
#Bean
public Database database() {
return new Database();
}
...
}
When using this technique, the Database is created twice because Spring doesn't seem to consider the #Configuration annotation on InfrastructureConfig. When using #Import, it works fine.
I don't want to use #Import because I want to mock my InfrastructureConfig like this :
#Configuration
public class TestConfig extends MainConfig {
#Override
public InfrastructureConfig infrastructureConfig() {
return mock(InfrastructureConfig.class);
}
}
Am I missing something or it is not supported ?
Thanks
When I first tried out Spring Java configuration I think I made the same assumption and was surprised when it didn't work.
I'm not sure this is the neatest way of solving this but I have used the following approach successfully.
To include that #Configuration class you can add this annotation to your MainConfig:
#ComponentScan(basePackages = "org.foo", includeFilters = {#Filter(filterType = ANNOTATION, value = CONFIGURATION)}, excludeFilters = {#Filter(filterType = ASSIGNABLE_TYPE, value = MainConfig)})
Since #Configuration classes are also candidates for component scanning this allows you to scan for all classes annotated with #Configuration. Since you're putting this annotation on MainConfig you need to exclude that with the ASSIGNABLE_TYPE filter since you'll get a circular reference.
I opened a Spring ticket SpringSource JIRA and they said that it is a known limitation and it is working as designed.

Resources