How to check two condition while using #ConditionalOnProperty or #ConditionalOnExpression - spring-boot

I need to check that two conditions are satisfied on a YAML property file, while creating a bean. How do I do that, as the #ConditionalOnProperty annotation supports only one property?

Since from the beginning of #ConditionalOnProperty it was possible to check more than one property. The name / value attribute is an array.
#Configuration
#ConditionalOnProperty({ "property1", "property2" })
protected static class MultiplePropertiesRequiredConfiguration {
#Bean
public String foo() {
return "foo";
}
}
For simple boolean properties with an AND check you don't need a #ConditionalOnExpression.

Use #ConditionalOnExpression annotation and SpEL expression as described here http://docs.spring.io/spring/docs/current/spring-framework-reference/html/expressions.html.
Example:
#Controller
#ConditionalOnExpression("${controller.enabled} and ${some.value} > 10")
public class WebController {

You might be interested in the AllNestedConditions abstract class that was introduced in Spring Boot 1.3.0. This allows you to create composite conditions where all conditions you define must apply before any #Bean are initialized by your #Configuration class.
public class ThisPropertyAndThatProperty extends AllNestedConditions {
#ConditionalOnProperty("this.property")
#Bean
public ThisPropertyBean thisProperty() {
}
#ConditionalOnProperty("that.property")
#Bean
public ThatPropertyBean thatProperty() {
}
}
Then you can annotate your #Configuration like this:
#Conditional({ThisPropertyAndThatProperty.class}
#Configuration

#ConditionalOnExpression("#{${path.to.property.one:true} and ${path.to.property.two:true}}")
both true values are the default value if property is not found.

Resolved the issue by using #ConditionalOnExpression for the two properties together.
#ConditionalOnExpression("'${com.property1}${com.property2}'=='value1value2'")
Wherein property value in configuration is as below.
Property 1
Name - com.property1
Value - value1
Property 2
Name - com.property2
Value - value2

Related

#Value annotation is only loading default - not using property file

Problem
I think that I havn't understood something properly because my #Value is always loading the default calue.
Java Code
So I have the following:
#Value("${disableQuerySecurityDebug:false}")
private boolean disableQuerySecurityDebug;
And this is set to false always.
Property file: application-disableQuerySecurityDebug.properties
I have a properties file called application-disableQuerySecurityDebug.properties.
And I have the following entry inside the file:
disableQuerySecurityDebugMne=true
And I run the application with the following profile: disableQuerySecurityDebugMne
I was expecting the value to be set to true, but it is always false.
Update
Based on deadpool's answer, I ended up with the following:
#Profile("disableQuerySecurityDebug") #Data
#Configuration
public class DisableSecurityConfig implements DisableQuerySecurityDebug {
#Value("${disableQuerySecurityDebug:true}")
private boolean securityDisabled;
}
#Profile("!disableQuerySecurityDebug") #Data
#Configuration
public class EnableSecurityConfig implements DisableQuerySecurityDebug{
#Value("${disableQuerySecurityDebug:false}")
private boolean securityDisabled;
}
public interface DisableQuerySecurityDebug{
public boolean isSecurityDisabled();
}
#Value annotation is only used to inject properties values into spring Beans from yml or properties file
This annotation can be used for injecting values into fields in Spring-managed beans and it can be applied at the field or constructor/method parameter level.
If you want to inject values based on profile specific then use #Profile on class
#Profile("disableQuerySecurityDebug")
#Configuration
public class Config {
#Value("${disableQuerySecurityDebug:false}")
private boolean disableQuerySecurityDebug;
}
You could also specify it on the command line by using the following switch:
java -jar demo.jar --spring.profiles.active=disableQuerySecurityDebug

Spring How to map list of custom pojo

In my application.yml file, I want to define a list of rules.
rules:
- name: abc
value: something
- name: edf
value: something
Then I want to define a service like this
#Service
public class MyService {
public MyService(#Value("${rules}") List<Rule> rules) {
}
}
For the Rule pojo, it's like this.
public class Rule {
public String name, value;
}
Currently, I'm facing these errors.
If I leave rules empty, it throws can't convert String to List<Rule>
rules: []
If I keep the values, it throws could not resolve placeholder 'rules'
I really don't know what I'm doing wrong here.
From Spring docs, I found this.
Using the #Value("${property}") annotation to inject configuration
properties can sometimes be cumbersome, especially if you are working
with multiple properties or your data is hierarchical in nature.
Spring Boot provides an alternative method of working with properties
that lets strongly typed beans govern and validate the configuration
of your application
Link: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties
At the end, I have to introduce another class.
#Configuration
#ConfigurationProperties(prefix="rules")
public class Rules {
public List<Rule> list;
}
Then I autowire it in MyService.

Evaluate property from properties file in Spring's #EventListener(condition = "...")

I would like to make the execution of an event handler dependent on whether or not a property is set to true in a properties file.
#EventListener(ContextRefreshedEvent.class, condition = "${service.enabled}")
public void onStartup() { }
However, this does not seem to work. I am getting the following error on startup:
org.springframework.expression.spel.SpelParseException: EL1043E:(pos 1): Unexpected token. Expected 'identifier' but was 'lcurly({)'
Is it possible to use a property from a properties file as a condition here?
The issue is condition argument is expecting a SPEL.
This works try it out.
In your bean where you have this #EventListener, add these lines
public boolean isServiceEnabled() {
return serviceEnabled;
}
#Value("${service.enabled}")
public boolean serviceEnabled;
change your declaration of evnt listener like this
#EventListener(classes = ContextRefreshedEvent.class, condition = "#yourbeanname.isServiceEnabled()")
public void onStartup() { }
change yourbeanname with the correct bean name .
I had the same annoying experience (with Spring Boot 2.4.2 on Java11).
In my case I had the boolean property in a #ConfigurationProperties class anyways in the same java file and still struggled a bit. First the #ConfigurationProperties need to be annotated as #Component to actually be a valid Bean and can be used in SpEL.
And I had to use the same long attributeName for the ConfigurationProperties in the Service itself and the EventListener Annotation for the Bean resolution to work fine. I needed some the ConfigurationProperties values also in another place of the Service, that's why they needed to be (Constructor) Autowired as well...
So this worked for me:
#ConfigurationProperties("my.custom.path")
#Component //Important to make this a proper Spring Bean
#Data //Lombok magic for getters/setters etc.
class MyCustomConfigurationProperties {
boolean refreshAfterStartup = true;
}
#Service
#RequiredArgsConstructor //Lombok for the constructor
#EnableConfigurationProperties(MyCustomConfigurationProperties.class)
#EnableScheduling
public class MyCustomService {
private final MyCustomConfigurationProperties myCustomConfigurationProperties;
#EventListener(value = ApplicationReadyEvent.class, condition = "#myCustomConfigurationProperties.refreshAfterStartup")
public void refresh() {
//the actual code I want to execute on startup conditionally
}
}

Choose component loading order

I have a spring boot with three #Component classes.
src.main.java
|_components
|_A
|_B
|_C
I need B and C executed before A can be executed. How do I specify that?
Should I use #DependsOn annotation? Or #Order? All answers online are for #Configuration and #Bean classes
You should be able to use #DependsOn like this:
#DependsOn({"b", "c"})
#Component("a")
public class A {
}
#Component("b")
public class B {
}
#Component("c")
public class C {
}
You can use #Order(<int>) notation when your components are advice and you want one advice to run before another advice.In that case the lower the number, the higher would be the precedence.
E.g.
Advice #Order(5) will have higher precedence over #Order(10).

Error while using parameter in #Scheduled in a spring 4.1 application

I have Spring 4.1 Application. I am trying to schedule based on value from property file. I have read this post. But I do not want the following way of EL inside #Scheduled
#Scheduled(fixedDelayString = "${my.fixed.delay.prop}")
public void readLog() {
...
}
Here is my class.
public class MyService {
#Value("${timerInMilliSeconds: 60000}")
private long timerinMilliSeconds;
public myService(){
}
#Scheduled(fixedRate = timerinMilliSeconds)
public void myTimer() {
//do stuff
}
}
I get this error.
The value for annotation attribute Scheduled.fixedRate must be a constant
expression
You can't do that; it's a limitation of the way annotations work - Strings in annotations have to be constants (they are stored in the class and can't be different for each instance).
By the way ${my.fixed.delay.prop} is not "EL", it's a property placeholder.

Resources