Reading Environment variable in SpringBootApplication [duplicate] - spring-boot

This question already has answers here:
Spring: How to inject a value to static field?
(5 answers)
Closed 3 years ago.
I have an environment variable RESOURCES_FOLDER. Which i want to read in a class inside my Springboot application
#Value("${RESOURCES_FOLDER}")
private static String resourcesFolder;
When I try printing the value it gives null instead of printing the actual path in my environment variable. Can someone please help me with this ??

Spring does not allow injecting values into static fields. You have a few options to circumvent this restriction:
Create a non-static setter. This is not a particularly good approach, as resourceFolder will be shared by all instances of a class. Nevertheless, you should be able to achieve it by:
public class SomeClass {
...
public static String resourcesFolder;
#Value("${RESOURCES_FOLDER}")
public void setResourcesFolder(String resourcesFolder) {
this.resourcesFolder = resourcesFolder;
}
...
}
Declare the field as non static. For this, ask yourself: do you really, really need the field to be static? Most of the time non-static field is good enough.
Create a separate #ConfigurationProperties class, declare fields as private non-static, create getters/setters for it and inject the class wherever you need the variable. This is a very composable and testable approach, which I would recommend, especially if you have quite a few related properties.
Alternatively, please refer to the other similar questions: 1, 2.

Add your environment variable name in the application.properties file by:
RESOURCE_FOLDER_PATH =${RESOURCES_FOLDER}
Here RESOURCES_FOLDER is the name of your environment variable.
Then access the environment variable in the java class by using #value annotation.
public class AccessEnvironmentVariable{
#Value("${RESOURCE_FOLDER_PATH}")
private String RESOURCE_FOLDER;
private void displayEnvironmentVariable(){
System.out.println("Your environment variable Resource Folder: "+RESOURCE_FOLDER);
}
}

Related

What 'final' keyword next to the field stands for?

In a legacy code, I'm working with, I found the following thing:
#Autowired
final lateinit var controller: CustomController
what does this final keyword mean here?
In a Kotlin documentation I found a short description about final keyword that is blocking overriding of the methods in open classes but no information about fields. Also - the class within which I found the line is not open
A final property or a method in Kotlin prevents overriding of the field / method. That being said, Kotlin by default considers a property or a method/function to be final unless specified by the keyword open. In your case, the final keyword is redundant.
Here's a small demo test case to illustrate the same.
open class Parent {
open val someValue = 0
final val otherValue = 13 // redundant modifier 'final' warning in Android Studio
}
class Child : Parent() {
override val someValue = 5
// override val otherValue = 19 // compile error
}
There is an interesting problem called Fragile Base Class in OOP and why some languages like Kotlin prefer final by default.
What you have there is a property, not a field.
It looks just like a field, as it would in Java; but in Kotlin, it actually defines a public getter method, a public setter method, and a private backing field*.
So the final modifier applies to the accessor methods, preventing those from being overridden in a subclass.  (As you say, the backing field itself can't be overridden anyway.)
As Siddharth says, final is the default in Kotlin, so you usually wouldn't need to specify it, though there are a few situations in which it would be needed — e.g. if it were already overriding something, or you were using the all-open or kotlin-spring compiler plug-ins.  (The use of #Autowired suggests that this is a Spring module, which probably explains why final is needed here.)  In any case, your IDE would probably indicate where it's not needed, e.g. by showing it greyed-out.
(* Only the getter is necessary; the setter isn't generated for a val, and the backing field isn't generated if you override the accessor(s) and they don't refer to it.)

What is best way to read properties from application.properties?

So, I have created configuration class with #Component and #ConfigurationProperties(prefix = "properties"), set default values for some of my application properties and changed some of them in application.yaml/properties
Now, I know I can access it using #Value("properties.*") but it can lead to having many variables which will be repetitive in another classes too
#Value("${properties.user-id-length}")
private int userIdLength;
I also can access my configuration class (as it is Spring Bean) through #Autowire it to variable in every single class I need make use of it. The cons for that is that more complex configuration class containing inner classes, which contain inner classes etc. will not look too great in code
#Autowired // Not recommended, but for simplicity
private MyConfigurationClass myConfigurationClass;
// some method
int userIdLength = myConfigurationClass.getUserIdLength();
String serverLocation = myConfigurationClass.getAmazon().getSes().getSenderEmailAddress()
Another way is to create additional helper class like Constant and set needed static fields with #Value but it can be time consuming and I'm not sure it is THAT different from first solution
public static int USER_ID_LENGTH;
#Value("${properties.user-id-length}")
private void setUserIdLength(int length){
Constant.USER_ID_LENGTH = length;
}
So, which aproach is the best? Or are there another ways to do that?
Well, not much of the feedback but in the meantime I figured out that using both #Value and #ConfigurationProperties leads to some problems.
MyProp.class
#Getter
#Setter
#ConfigurationProperties(prefix = "prop")
public class MyProp{
private String default = "Default String"
}
SomeClass.class
#Component
public class SomeClass.class{
#Value("${prop.default}")
public String message;
}
Above example causes Exception BeanCreationException
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'someClass': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'prop.default' in value "${prop.default}"
Explanation to this can be found here in #davidxxx's post and occurs unless we set all fields in application.properties. Because of that my first proposal cannot be done in the terms I thought it can be done and explanation was not easy to find, so I hope it will help someone one day.

Spring custom annotation for this behavior

I have code like this.
#org.springframework.stereotype.Component("studentInfo")
#org.springframework.context.annotation.Profile("studentInfo")
public class CustomStudentInfo{
As you can see i have the component name and the same profile i mean i just want to set this class as a bean only when the profile is set and fact this is working but is kind annoying to type this on 2 lines my question is can i have this on a custom annotation i mean a annotation that help me just to write.
#CustomSpringAnnotation("studentInfo")
public class CustomStudentInfo{
Thanks and sorry if the question is plain.
You can "incorporate" the spring annotations into a custom one like (source/proof: SpringBootApplicaiton source code):
package my.package.annotations;
#org.springframework.stereotype.Component("studentInfo") // needs "constant expression" here
#org.springframework.context.annotation.Profile("studentInfo") // .. and here!
public #interface MyCustomSpringAnnotation { ...
// but here you have a problem,
// since you cannot pass (at least not to the above annotations,
// ... but maybe dynamically *hack* into the spring context):
String value() default ""; //?
}
...then you could use it like:
#MyCustomSpringAnnotation
public class CustomStudentInfo { // ...
but with the fixed "studentInfo" it is no improvement (but the contrary).
The probably "most spring-like" and best solution (no stress with "too many annotations") is: To consume "studentInfo" from a "visible" (static) final variable (probably best in the affected class):
#org.springframework.stereotype.Component(CustomStudentInfo.PROFILE_NAME)
#org.springframework.context.annotation.Profile(CustomStudentInfo.PROFILE_NAME)
public class CustomStudentInfo {
public static final String PROFILE_NAME = "studentInfo";
// ...

Can you or can't you use a property from a config file when using Spring #Condition?

I have read pretty much everything I can find on StackOverflow and other sites and I don't see a definitive answer anywhere.
I have a class that implements #Condition that I use in a #Configuration file to conditionally load some beans. I am doing something like this:
public class MyCondition implements Condition {
#Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metdata) {
String property = context.getEnvironment().getProperty("some.prop.from.file");
boolean enable = Boolean.parseBoolean(property);
return enable;
}
}
When debugging I see that getting the property from the environment always returns null, even though the property is injected in other beans using #Value.
So my question can you or can't you attempt to get a property value from a file within a #Condition class? Can you only get System properties? I would think that this is a common use case that I would think Spring could handle.
Had to add the property to application.properties and not the other property files that are loaded during startup.

Using #Value annotation with Spring and SPeL

I am trying to find a way to do the following in my spring boot 1.5 application.
I have a variable who's value is dynamic meaning it comes in from an external system.
String name = "abc"; //gets set externally
I want to try and use the name's value to lookup my property file and see if there is a matching property defined. something like..
#Value("#{myClassName.name.concat('something')}")
String propertyValue;
Now my application.property file has the following property set
assume name has the value "abc"
property file contents:
abc.something:abcValue
Now, when i try to access the value of the variable propertyValue it gets set to the value abc.something and not abcValue.
I probably think I cannot use #Value with #{} to get to that, I was wondering if there was a way to to use #{} inside ${} so that I goes and fetches the property value after calculating the name of the property using #{}.
Let me know if you need more details please.
A bean life-cycle requires properties to be resolved at compile time. So, #Value requires constant parameter.
You can use Environment bean to access your properties programmatically.
import org.springframework.core.env.Environment;
#Service
public class Serivce {
#Autowired
private Environment environment;
public String getProperty(final String keyPart) {
String key = "build.your." + keyPart;
return environment.getProperty(key)
}
}
By the way you can use #('${spring.some.property}') in SpEL to access placeholder.
// This is valid access to property
#Value("#('${spring.some.property}')")
private String property;

Resources