How to decide which value a property takes in a spring bean based on a condition. - spring

Can I set the property of "contentCaptureRegEx" to a regular expression say "x" based on whether an external value of an enum such as ContentType.docType.equals("regtext") ELSE set it to y.
SO,
if(ContentType.docType.equals("regtext"))
constructor-arg is x
else
constructor-arg is y

You can do things like that with the Spring Expression Language (SpEL).
There is a dedicated chapter for pretty much exactly what you want:
7.4 Expression support for defining bean definitions

Related

Spring Framework: Is there a list of "reserved words" or "Predefined Variables" about SpEL?

For Spring Framework where is the complete list of reserved words or Predefined Variables about SpEL with their respective explanation? It such as for:
environment
systemProperties
systemEnvironment
For example in this valuable tutorial is just shown the 2 latest of the list shown above
Spring Expression Language (SpEL) Predefined Variables
Consider if Spring 6 added more of them, it as an improvement, therefore could be more.
Yes, I did do a research - currently for version 6 - at the official documentation at:
Spring Expression Language (SpEL)
But does not contain the reserved words.
There isn't really a list of reserved words for SpEL like what you are looking for, at least not in generic SpEL parsing and evaluation.
There is default SpEL support in the application context and notably in #Value annotations, as documented in the reference guide: Expressions in Bean Definitions.
Thi is provided via a SpEL StandardEvaluationContext which uses the beanFactory as the root object. Or rather, it uses a BeanExpressionContext view of the bean factory plus a dedicated PropertyAccessor is added to this evaluation context so that every beanName in the bean factory is considered a property of that root object during evaluation.
As a result, if a bean named foo is registered in the factory, #Value("#{ foo }") will be interpreted as "the property foo of the root object", which resolves to the aforementioned bean.
So systemProperties/systemEnvironment and environment are not really predefined SpEL variables, but rather standard beans in an AbstractApplicationContext.

Spring Boot | spring EL using with annotation attribute value to set any random value using properties

I am using one existing spring boot annotation and want to set the attribute value dynamically using spring expression and want to have one constant value as a prefix,
sample Code snippet
#KafkaListener(topics="${topic.name}", groupId="#{'consumergroup' + (int)(java.lang.Math).random() }")
public void consumerMethod(){
}
I want to create a new consumer group for each new container using the above kind of implementation.
But I am facing the below exception.
Expression parsing failed; nested exception is
org.springframework.expression.spel.SpelParseException: EL1041E: After
parsing a valid expression, there is still more data in the
expression: 'lparen(()
kindly help me to either use template spring EL or any other way I can use to set dynamic consumer group id with the constant prefix.
groupId="#{'consumergroup' + (100 * T(Math).random()).intValue() }"
There is no cast operator in SpEL. Just because it uses a ConversionService internally to convert from one type to another. I use intValue() any way because the result of random() is Double (not double) - SpEL does coercion into type wrappers for API convenience.
The java.lang is imported into SpEL context automatically. No need to add it for Math type.
100 *. See Math.random() JavaDocs: it returns the value between 0.0 and 1.0. So, casting to int would always bring you only 0. The casting doesn't do rounding.
Also see Spring Boot configuration properties feature where you can use random value for the groupId:
spring.kafka.consumer.group-id=consumergroup${random.int}
https://docs.spring.io/spring-boot/docs/current/reference/html/features.html#features.external-config.random-values

How to load a bean only if the property value is other than a fixed value

I am facing a situation in which a bean should be created by the application context only if the property contains any other value than a specific value.
i.e... one property file.path= /specific/path, If the value is other than this then bean should be loaded.
I can see that there is #ConditionalOnProperty (name="file.path", havingValue="....") out of the box but in my case, I am looking a property like havingValueOtherThan="..." or similar kind of property or annotation if it is there out of the box in the spring.
There are many possible options, besides profiles that were stated in comments, here are 2 options:
Option 1
Use #ConditionalOnExpression with any SPeL expression you wish
Option 2
You can always create your own conditional annotation with any logic:
Create an annotation that will depict your own business case with a #Conditional on it.
Then Implement the conditional logic of your choice. Here is an example of achieving this.
BTW, the profiles that mentioned in comments are using Conditionals engine under the hood (The #Profile annotation has a #Conditional({ProfileCondition.class}) in its definition since Spring 4.x)

Optional environment variables in Spring app

In my Spring Boot app's application.properties I have this definition:
someProp=${SOME_ENV_VARIABLE}
But this is an optional value only set in certain environments, I use it like this
#Value("${someProp:#{null}}")
private String someProp;
Surprisingly I get this error when the env. var doesn't exist
Could not resolve placeholder 'SOME_ENV_VARIABLE' in string value "${SOME_ENV_VARIABLE}"
I was expecting Spring to just set a blank value if not found in any PropertySource.
How to make it optional?
Provide a default value in the application.properties
someProp=${SOME_ENV_VARIABLE:#{null}}
When used like #Value("${someProp}), this will correctly evaluate to null. First, if SOME_ENV_VARIABLE is not found when application.properties is being processed, its value becomes the string literal "#{null}". Then, #Value evaluates someProp as a SpEL expression, which results in null. The actual value can be verified by looking at the property in the Environment bean.
This solution utilizes the default value syntax specified by the PlaceholderConfigurerSupport class
Default property values can be defined globally for each configurer
instance via the properties property, or on a property-by-property
basis using the default value separator which is ":" by default and
customizable via setValueSeparator(String).
and Spring SpEL expression templating.
From Spring Boot docs on externalized configuration
Finally, while you can write a SpEL expression in #Value, such
expressions are not processed from Application property files.
This work for me:
spring.datasource.url=jdbc:mysql://${DB_IP:localhost}:3306/app
spring.datasource.username=${SPRING_DATASOURCE_USERNAME:mylocaluser}
spring.datasource.password=${SPRING_DATASOURCE_PASSWORD:localpass}
Because this is probably interesting to others who come here, you can override any properties file w/ an env variable implicitly. Let's say you have property.
someapp.foo
Then you can define an env variable SOMEAPP_FOO (capital letters and . -> _ ) and spring will implicitly set the property from the env. variable.
Described further here: https://hughesadam87.medium.com/how-to-override-spring-properties-with-env-vars-82ee1db2ae78

Injecting Spring Spel Expressions into bean properties

How can I inject a String meant to be a spel expression into an app context bean property without having the context interpret the string property as a spel resolvable value.
<bean id="x" p:spelExpression="${#code}/${#value}"/>
I want the the class's setter property to take a string argument and creates a reusable SpelExpression as a private property of the bean, however, the spring application context appears to be recognizing the #variableName as spel expressions and attempting to interpret the expression at initialization time.
I find Spel interpretation to be very useful, but would like to to use spel internally as well, with expression injection into my own beans.
Is it possible to disable the Spel Interpretation of for a bean, or even for a whole context file (maybe with some namespace shenanigans) without having to disable or modify spel expression resolution for the whole application context.
The #{...} are markers for spel interpreter in xml context.
I don't undestand : ${#code}/${#value}. Good is #{code/value}, as I understand things, if code and value are id.
So, if you want a spel expression without interpret it, put string code/value in xml.

Resources