Spring managed StatementInspector - spring

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

Related

Custom AutoConfiguration is not detected by spring application

Hi I created one custom auto configuration library as following:
#Configuration
#EnableConfigurationProperties(RefreshProperties.class)
public class PropertiesRefresherAutoConfiguration {
public static final String AUTO_REFRESH_ENABLED = RefreshProperties.PROPERTIES_AUTO_REFRESH + ".enabled";
#ConditionalOnProperty(
name = PropertiesRefresherAutoConfiguration.AUTO_REFRESH_ENABLED,
havingValue = "true"
)
#Bean
public Refresher getRefresher(){
return new Refresher();
}
}
I have added spring.factories in META-INF with content:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.bla.PropertiesRefresherAutoConfiguration
What I want to achieve is when I use dependency of this custom library and I set application.yaml properties:
properties:
refresh:
enabled: true
Refresher instance should automatically be created. In Refresher class there is one method that should be called on application start
#PostConstruct
public void startTask() {
//do something
}
In my main application I've added dependency to it and defined properties in application.yaml but Refresher Bean is not created and startTask() method is not called
I have tried to remove #Conditional annotation in order to create instance of Refresh every time, but that didn't help.

What is the best way to select bean implementation from application.yaml

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)

Spring Boot: use autowired constructor with class from configuration file

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

How to dynamically create spring beans based on properties file and then autowired them to use in another bean?

I want to create beans after reading some properties from the application.yaml file and then want to inject them to other beans. How this can be achieved?
You might want to use #Value annotation to inject properties from application.yaml as fields into your class.
You probably need to create your bean on your own, e.g.:
#Bean
public YourBeanClass createBean() {
return new YourBeanClass()
}
You can then inject properties from your application.yaml with the #Value annotation in the bean creation method:
#Bean
public YourBeanClass createBean(#Value("${path.to.key}") String propertyValue) {
return new YourBeanClass(propertyValue)
}
When you create a bean like that, you can simply autowire it, e.g. in a service:
#Service
public class ServiceName {
public ServiceName(YourBeanClass autowiredType) {
}
}
And the bean YourBeanClass will be autowired into the constructor of your ServiceName service class for usage. The dependency resolution will be done by spring automatically.
Here's an example, that creates a bean for each value in a property list. This creates a different number of implementations depending on property values:
#Configuration
public class MineralTypeCreator {
#Bean
public List<MineralType> mineralTypes(final MineralProperties mineralProperties) {
return mineralProperties.getTypeIds().stream().map(GenericMineralType::new).collect(Collectors.toList());
}
private class GenericMineralType implements MineralType {
public GenericMineralType(final String id) {
this.id = id;
}
// ...
Having the properties passed into the #Bean-method is still nice, even if you only need to create a single bean.

#ConditionalOnProperty not reflected when viewing #Autowired beans in Intellij IDEA

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?

Resources