I'm starting with Spring Boot, so am first going through the Spring Boot reference Guide
I'm currently having a look at the Externalized Configuration
My goal is to have a simple Component to read the value out of application.properties, which, if I'm not mistaken, should be loaded automatically, without any further configuration.
The example in the reference is as follows:
import org.springframework.stereotype.*
import org.springframework.beans.factory.annotation.*
#Component
public class MyBean {
#Value("${name}")
private String name;
// ...
}
with the following explanation:
On your application classpath (e.g. inside your jar) you can have an
application.properties that provides a sensible default property value
for name. When running in a new environment, an application.properties
can be provided outside of your jar that overrides the name; and for
one-off testing, you can launch with a specific command line switch
(e.g. java -jar app.jar --name="Spring").
Seems simple enough. So, I have created following Component:
#Component
public class Admin {
#Value("${name}")
private String name;
public String getName(){
return "ReadName_" + name;
}
}
And, in my recources folder, the following application.properties file (which, I verified, is copied into my .jar)
name=myName
So, if I follow the logic in the reference (which is also mentioned in this post: Read application.properties, and run my application.
The code compiles and runs without error, but each time I print the value of getName() from an instance of Admin on my screen, it just gives a "ReadName_null" instead of "ReadName_myName", the value in the properties file.
As the reference states, in 24.2:
By default SpringApplication will convert any command line option
arguments (starting with ‘--’, e.g. --server.port=9000) to a property
and add it to the Spring Environment. As mentioned above, command line
properties always take precedence over other property sources.
So, just to check, I run my .jar file with both the application.properties with the key present, and add --name=passedName as a command line argument.
Even though this value should be automatically added to the environment, and overrule anything (or in my case: nothing) that is currently there, and I expect the getName() to return "ReadName_passedName", it still returns "ReadName_null".
EDIT: I print the command line arguments I pass while booting, so I know that the argument is indeed read by the system as "--name=passedName"
Is there anything obvious I'm missing here?
If you do new Admin (), you are creating a new instance of Admin and not a spring bean. So you don't get any advantages which spring provides(for example Dependency Injection) And hence the value is null.
Instead you should Autowire it in your class. This way spring will inject the instance of Admin that it has created (with injected values)
#Autowired
Admin admin;
And before you ask, yes by default all beans are singleton(unless specified otherwise). So no matter whereever you Autowire Admin, you will get the same instance.
This is a broad topic, you should read about it.
Related
I am using Spring Boot 2.7.5
While using #Value annotation to inject values form application.properties file, getting null assignment to variables under #Configuration class.
Example:
#Value("${file.name}")
private String fileName;
Here I am getting seeing assignment to variable fileName
Ideally it should assign value '${file.name}' if key isn't matching. But null assignment means something is breaking in the project (at least I think so, I need experts comments on this).
Same thing is working in another project but not in this particular.
Let me know if my question is not elaborative enough and will try to explain in detail
My question is, why is it working in other project but not in this one. What configurations could go wrong which I need to check. Have gone through multiple stackoverflow solutions and verified all below:
application.properties file spell check
#Configuration annotation at top of class where #Value is being used
key value pair assignment and spell check of all keys
no issues with library imports
Tried to add #PropertySource("classpath:foo.properties")
import org.springframework.beans.factory.annotation.Value;
resources folder is correctly marked as 'Resources Root'
Temporary Alternative
private Properties props = new Properties();
props.load(VaultCredential.class.getResourceAsStream("/application.properties"));
props.getProperty("file.name");
Can you please try with below code
private String PathForImageGallery;
#Value("${file.name}")
public void PathForImageGallery(String PathForImageGallery) {
this.PathForImageGallery = PathForImageGallery;
}
Apologies everyone. I was calling a class where was trying to bind #Value with properties and calling it in main() method of #SpringBootApplication.
Totally missed that main() method will not load property values until spring application is up and running.
Working solution if some one want to load properties before even running a SpringBoot application -
private Properties props = new Properties();
props.load(VaultCredential.class.getResourceAsStream("/application.properties"));
props.getProperty("file.name");
PS - Thanks all for your efforts. Admins can close it down if considered as non-logical question.
I have a .properties file with bunch of properties in it. Here's an example:
mes.mail.debug=true
cookie.sso.domain = .stuffStuff.com
blabla.endpoint = blabla.com
test.value.property = myValue
The problem is with the last one (Which I have just added to the project we're working on).
I read the properties using #Value("${PropertyName}") annotation and it was working perfectly until lately, when I use the same thing, the variable gets the propertyName instead of its value:
#Value("${test.value.property}")
private String mProperty;
so, mProperty gets "test.value.property" where what I'm looking for is for it to get "myValue".
What's happening exactly? Is there something wrong with my project? I have tested in my friend's computer and it works perfectly.
By the way, i'm using Spring Tool Suite.
EDIT: It turns out that it doesn't detect the changes I make in the properties file. So if I change an old property's value; it acts as if nothing happened.
Does anyone has any idea why it's doing like this?
When you declare a Property Placeholder Configurer to load the properties files, you can set it to ignore unresolvable placeholders.
This means that if the property you are injecting with #Value is not found, its name (or key) will be assigned to the variable.
In your case, this option is enabled and the file that has been loaded by the application is not the one you are editing.
To see from where the file is been loaded, check the placeholder configurer location property.
I would like to have properties, that I can reference via #Value in spring beans, that can only be created dependend on other properties.
In particular I am having a property, that describes the file system location of a directory.
myDir=/path/to/mydir
And by convention, there is a file in that directory, that is always called myfile.txt.
Now i want to have access to both, the directory and the file, via #Value annotations inside my beans. And sometimes I want to access them as Strings, sometimes as java.io.Files and sometimes as org.springframework.core.io.FileSystemResource (which by the way works very well out of the box!). But because of that concatenating Strings on demand is not an option.
So what I of course could do is just declare both, but I would end up with
myDir=/path/to/mydir
myFile/path/to/mydir/myfile.txt
and I would like to avoid that.
So I came up with an #Configuration class, that takes the property and adds it as new PropertySource:
#Autowired
private ConfigurableEnvironment environment;
#Value("${myDir}")
private void addCompleteFilenameAsProperty(Path myDir) {
Path absoluteFilePath = myDir.resolve("myfile.txt");
Map<String, Object> props = new HashMap<>();
props.put("myFile, absoluteFilePath.toString());
environment.getPropertySources().addFirst(new MapPropertySource("additional", props));
}
As you can see, in my context I even created a PropertyEditor, that can convert to java.nio.file.Paths.
Now the problem is, that for some reason, this "works on my machine" (in my IDE), but does not run on the intended target environment. There I get
java.lang.IllegalArgumentException: Could not resolve placeholder 'myFile' in string value "${myFile}"
Spring can combine properties
myDir=/path/to/mydir
myFile=${myDir}/myfile.txt
You can also use a default value without defining your myFile in the properties at first:
Properties file
myDir=/path/to/mydir
In class:
#Value("#{myFile:${myDir}/myfile.txt}")
private String myFileName;
Spring expressions can be used to refer the properties.
In my example it was
query-parm=QueryParam1=
query-value=MyParamaterValue
Now while binding them in Spring Bean.
#Configuration
public class MyConfig {
#Value("${query-param}${query-value}")
private String queryString;
}
Above code will inject QueryParam1=MyParamaterValue to the variable queryString.
I want to use Groovy scripts to utilize some java classes that are have spring annotations along the lines of the following:
#Component
class SomeUtility {
#Value("${foo}")
public string String fooValue;
}
Ideally I would like to configure foo in an application.properties file in the same manner as done in spring-boot
I have tried adding spring to the classpath and running something like this in a groovy script:
def ctx = new GenericApplicationContext()
new ClassPathBeanDefinitionScanner(ctx).scan('dylan')
ctx.refresh()
def b = ctx.getBean(SomeUtility)
println b.fooValue
but the output is ${foo} whether a properties file is in place or not - so I guess the #Value value processing is not happening.
I have also tried adding spring-boot to the classpath and running the above script - but I suspect I am not actually triggering spring-boot in that case.
Is there any way that I can do this - what I want is essentially to be able to configure the values easily then get hold of the components in a groovy script.
Since you are using Groovy, you need to do single quote #Value('${foo}'), as double quotes will be picked up by Groovy as a GString before Spring has a chance to look at its own context.
The purpose of my code is to load some settings value from a *.properties file so that I later can use these values in some if-statements in my code. I want to load in some list-structure, but since that seems hard, an array will do. I have not really gotten that far, since I am stuck at the trivial matter of loading just a String from the properties file.
When I try to debug my code that is using some spring specific data. I get some interesting behaviour, pointing on the definition in the code right above the breakpoint gives me that the variable value is null.
#Value(value = "${ViewableReportFilter.allStates.verify}")
String verifyStringStates;
public ViewableReportFilter() {
viewStates = null;
log.debug("Read in properties for states: verify:" + verifyStringStates);
/*BREAKPOINT HERE*/
in my my.properties file:
ViewableReportFilter.allStates.verify=ONHOLD
And my config to use the properties-file:
<context:property-placeholder location="classpath:properties/my.properties" order="1" ignore-unresolvable="true" />
Spring can't set the fields of an object before that object is created. The first thing Spring does is use reflection to instantiate your class. It'll use either Class#newInstance() or use Constructor#newInstance() depending on the context. Only when the constructor has finished its work and returned can Spring, again using reflection, set the value of fields.
An alternative is to put a #Value annotated parameter in the constructor parameter list and set your field inside your constructor from the argument that's given to it by Spring.
public ViewableReportFilter(#Value String verify) {
this.verifyStringStates = verify;
...
Go through the Spring documentation for its IoC container. It explains all of this in much detail.
Updated the constructor, and added Autowire annotation. No changes in the properties file, no XML.
String arrayOfStrings;
#Autowired
public ViewableReportFilter(
#Value("${TMSViewableReportFilter.allStates.verify}") String[] verifyStringStates) {
arrayOfStrings = verifyStringStates;
public logViewableReportFilter() {
log.debug("Read in properties for states: verify:" + arrayOfString);
}
Try using this:
#Value(value = "${allStates.verify}")
And in your property my.properties:
allStates.verify=ONHOLD