Let's say I have #Configuration class which registers bean of type RestClient conditionally using #ConditionalOnProperty.
#Configuration
public class RestClientConfig {
#Bean("restClient")
#ConditionalOnProperty(prefix = "rest.client", name = "enabled", havingValue = "false", matchIfMissing = true)
public RestClient restClient(RestProperties properties) {
return new HttpRestClient(...);
}
#Bean("restClient")
#ConditionalOnProperty(prefix = "rest.client", name = "enabled", havingValue = "true")
public RestClient mockRestClient(RestProperties properties) {
return new MockRestClient();
}
}
When I run this application, everything works. Implementation of given type is chosen correctly when I autowire RestClient in another bean.
However, when I view this setup in Intellij IDEA, it reports:
Is there a way to instruct Intellij to know about #ConditionalOnProperty or do it in different way in Spring Boot?
Related
How can I create a "Spring managed" StatementInspector to enable certain Springboot functionality like Autowiring classes and referencing spring properties via #Value within the StatementInspector class.
The current method I use to register a StatementInspector via a configuration property (below) does not allow for these Spring functionalities.
spring:
jpa:
properties:
hibernate:
session_factory:
statement_inspector: x.x.SqlStatementInspector
A possible solution:
Configure a HibernatePropertiesCustomizer-bean
#Bean
public HibernatePropertiesCustomizer hibernateCustomizer(StatementInspector statementInspector) {
return (properties) -> properties.put(AvailableSettings.STATEMENT_INSPECTOR, statementInspector);
}
Provide a or more conditional StatementInspector-beans based on a property value.
example: OneStatementInspector is only created when demo.statement_inspector is equal to one
#Component
#ConditionalOnProperty(prefix = "demo", name ="statement_inspector", havingValue = "one" )
public class OneStatementInspector implements StatementInspector {
#Value("${demo.my-value}") String myValue;
#Override
public String inspect(String s) {
// myValue is available here
...
}
}
application.properties
demo.my-value=my autowired value
demo.statement_inspector = one
If the configuration of a StatementInspector is optional ( demo.statement_inspector is not mandatory) there are multiple options:
Make one of the possible StatementInspector the default (match if property is missing) #ConditionalOnProperty(prefix = "demo", name ="statement_inspector", havingValue = "...", matchIfMissing = true )
Make HibernatePropertiesCustomizer-bean optional:
#Bean
#ConditionalOnProperty("demo.statement_inspector")
public HibernatePropertiesCustomizer hibernateCustomizer(StatementInspector statementInspector) {
...
}
Provide a default bean as #dekkard suggests:
#Bean
#ConditionalOnMissingBean(StatementInspector.class)
public StatementInspector emptyInspector() {
return EmptyStatementInspector.INSTANCE;
}
note: No need to set spring.jpa.properties.hibernate.session_factory.statement_inspector
I have a spring boot application in which I want to Autowire a bean for which implementation is specified in application.yaml. What is the best way to achieve it?
#Component
public class FooFormatter implements Formatter {}
#Component
public class BarFormatter implements Formatter {}
public class MyService {
#Autowired
#Qualifier("value_from_config")// The implementation is specified in application.yaml file
private Formatter formatter;
}
The best way to achieve it is to use #ConditionalOnProperty.
So given the followings :
#Component
#ConditionalOnProperty(prefix = "app.formatter", name = "impl", havingValue = "foo",matchIfMissing = true)
public class FooFormatter implements Formatter {
}
#Component
#ConditionalOnProperty(prefix = "app.formatter", name = "impl", havingValue = "bar")
public class BarFormatter implements Formatter {
}
Then to enable FooFormatter only , configure the application properties as :
app.formatter.impl=foo
To enable BarFormatter only , configure the application properties as :
app.formatter.impl=bar
If no app.formatter.impl is defined in application properties , it will default to FooFormatter (because of the matchIfMissing = true)
I have a Spring Boot 2.3 application with a controller:
#RestController
public class StatusController {
private final ServerStatusCheck serverStatusCheck;
private final ServerStatusMapper serverStatusMapper;
#Autowired
public StatusController(AService aService, ServerStatusMapper serverStatusMapper) {
this.serverStatusCheck = aService;
this.serverStatusMapper = serverStatusMapper;
}
// (...)
}
The class AService implements the interface ServerStatusCheck. There is also a BService class, also implementing ServerStatusCheck interface.
What I need to do: the injected AService object should be configurable in a configuration file, so that the service injected is either "AService" or "BService", depending on the configuration file values. What is the best way to achieve this using Spring Boot? If possible, I would like to keep the constructor-based autowiring (instead of field-based autowiring).
You can create the different beans in a configuration class with condition like https://reflectoring.io/spring-boot-conditionals/
#Configuration
public class ServiceConfiguration {
#ConditionalOnProperty(value="service.a.enabled", havingValue = "true", matchIfMissing = true)
public ServerStatusCheck serverStatusCheckA() {
return new AService();
}
#ConditionalOnMissingBean
#ConditionalOnProperty(value="service.b.enabled", havingValue = "true", matchIfMissing = true)
public ServerStatusCheck serverStatusCheckB() {
return new BService();
}
}
and then wire the bean into the constructor
I couldn't find the solution in any other questions posted here.
I have multiple beans with conditionalOnProperty invoking different beans like below
#Bean
#ConditionalOnProperty(name = 'invoke.bean', havingValue = 'bean1', matchIfMissing = true)
SupportingInterface bean1() {
return new Bean1()
}
#Bean
#ConditionalOnProperty(name = 'invoke.bean', havingValue = 'bean2')
SupportingInterface bean2() {
return new Bean2()
}
in the yml file if I do invoke.bean: bean1 or invoke.bean: bean2 individually works fine but if I want to use both invoke.bean: bean1, bean2 how can I do it?
A good practice is defining a service as an interface and its implementation on a class.
Assuming I have 2 classes which implement the same interface, and I'd like to differentiate them according a property (not to a profile). I mean, if I have #Autowire private MyServiceInterface myService; I'd like to receive an instance of PotatoServiceImpl if I have myproperty=potato or an instance of TomatoServiceImpl if I have myproperty=tomato.
I'm not using profiles.
P.S. When I say a property,I mean a property in application.properties
Look:
public interface MyInterface {
}
#Component
#ConditionalOnProperty(prefix = "myproperty" havingValue = "potato", matchIfMissing = false)
public class MyPotatoImpl implements MyInterface {
}
#Component
#ConditionalOnProperty(prefix = "myproperty" havingValue = "tomato", matchIfMissing = false)
public class MyTomatoImpl implements Myinterface {
}
#Component
public class Consumer {
#Autowire
private MyInterface tomatoOrPotato; //depending on property myproperty value
}
This is for me a very elegant solution to implement the strategy creational design pattern spring styled.
Look here for docs about #ConditionalOnProperty annotation.