In my project i use Spring Integration Sftp. I tried configure remoteDirectoryExpression with spEL, but the main problem, that SFTP folder value i store in application properties as sftp.folder=/test/.
handler.setRemoteDirectoryExpressionString("\'" + properties.getFolder() + "\'" + ".concat(headers['region'])");
properties.getFolder() is a class with #ConfigurationProperties.
This solution works fine, but i do not like it. So, it is possible somehow to write spEl expression without my curly braces?
but can`t get property from application.properties using expressions like ${}
That's right: you can't do that on this setter level because there is no properties placeholder knowledge because it is already a plain Java call.
You can do what you want via a #Value annotation on the property of the #Configuration class on or via #Bean method parameter:
#Bean
FtpMessageHandler ftpMessageHandler(#Value("'${sftp.folder}' + headers['region']") String remoteDirectory)
Related
I have a Spring Boot application, using Intellij, and am trying to use the #Value annotation in order to get an environment variable from my application.properties.
My application.properties looks like this
server.port=27019
web.entrance.id=63d284ec
Using debugger, I can see that the value of entranceId is successfully retrieved from application.properties, but the same variable is always null in the String.format and my WebUrl has the string 'null' in it and I don't understand why.
#RestController
public class Controller {
#Value(("${entrance.id}"))
private String entranceId;
String WebUrl = String.format("http://localhost:27019/%s", entranceId);
Can someone explain if there is some detail I'm missing why this happens?
Thank you
Your thinking is wrong. Spring will process the #Value after the object has been constructed, whereas your field is being initialized as soon as the class is being constructed (so during constructing).
Basically what Spring does
new Controller()
Detect #Value and with reflection set value
Any construction callbacks (like #PostConstruct).
Your field is being filled at step 1 not after step 2. At which point the #Value hasn't yet been processed.
If you want to set the value you need to do that in an #PostConstruct method or use constructor injection to construct the URL.
#Value("\${datasource.host}")
private val host: String = ""
I wrote the following code in KOTLIN and it worked fine.
I don't understand how the host was injected into the host.
In my knowledge, the value should not be injected because the host variable is val.
How does this code work?
Short answer: Spring is magical!
For a Kotlin property, val doesn't necessarily mean that the property is constant. (It's not an exact equivalent of Java final here.) It simply means that there's a get() method but no set() method.
That leaves open the possibility for the value to change some other way. (For example, the property could have a custom getter which returned different values.)
I'm not sure quite how Spring works its magic; it may be able to set the property's backing field directly, or it may create a hidden subclass which can. In any case, it's perfectly capable of setting val properties. (You can also see this in Hibernate.)
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
I have some code to load a value as such in my Spring application:
#Component
public class MyElasticRestService {
#Value("${elasticApi.baseURL}")
private String elasticApiBaseUrl;
According to the Spring docs, I should be able to use a relaxed binding that comes from an uppercase environment variable such as ELASTIC_API_BASE_URL or ELASTICAPI_BASEURL. But I'm confused which is correct. Both don't seem to work so I am wondering how to debug what is actually picked up.
I've loaded Spring Boot Actuator to view the configprops endpoint. But it doesn't have anything on the elasticApi prefix.
What should the correct environment variable be and how can I see how it gets translated and picked up by the application?
The #Value annotation doesn't support relaxed bindings. Therefore you could use a class annotated with #ConfigurationProperties or you use a RelaxedPropertyResolver to get the value from the environment.
According to https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-vs-value, it is now very possible simply with #Value as long as you use kebab-case (all lower case with dash) for the name e.g. #Value("config.refresh-rate")
Instead of trying to make it an UPPER_SNAKE_CASE, you can put it in your application.yaml file, this way:
elasticApi.baseURL: ${ELASTIC_API_BASE_URL:defaultvalue}
or this way doesn't really matter:
elasticApi:
baseURL: ${ELASTIC_API_BASE_URL:defaultvalue}
I have Mule Configuration that defines
<context:property-placeholder location="classpath:/mule-sw.properties"/>
And I also have a custom component class in Java which use #Lookup annotation on one of my field
#Lookup("file-path")
private String path;
Considering my "mule-sw.properties" is like this
file-path=C:/hello.txt
After I start up the Mule App, I always get Deploy Exception
org.mule.api.expression.RequiredValueException: "Required object not found in registry of Type "class java.lang.String" with name "file-path" on object "class component.CustomComponent"
I also tried to change the #Lookup("file-path") with #Lookup("${file-path}") with no success.
Anyone can give me a better solution ?
Any help is kindly appreciated.
Thanks
The #Lookup annotation is designed to retrieve objects from the registry , whereas what you are trying to do is to assign a value to an attribute of your custom component.
There are 2 way to do that:
1) Through property injection, i.e. you declare your component like the following:
<custom-component class="org.myCompany.CustomComponent">
<property name="file-path" value="${file-path}" />
</custom-component>
2) Through Spring EL support and the #Value annotation, i.e. you annotate your attribute in the following way
#Value("${file-path}")
private String path;
I'd recommend the first approach since it is easier to maintain from a flow perspective
#Value("#{'${file-path}'}")
private String path;
Give this a shot. I think you need to wrap it in an EL block #{} for it to be properly recognized.