How to use environment variables with application.properties in spring boot? - java

I am trying to configure my application.properties to take the value from the system variables I've set. The problem is that it won't work no matter what I do. I've tried using a custom class configuration with DataSource from this example still no success. All I was able to find on stackoverflow are links pointing to the official docs. Unfortunately that's a dead end since it doesn't say anything and there are no examples for what I'm trying to achieve. I also found examples like spring.datasource.url = ${SPRING_DATASOURCE_URL} and everyone is saying that spring will know how to automatically take the environment variables. Well surprise, it doesn't.
I also found examples like spring.datasource.password = ${SPRING_DATASOURCE_PASSWORD:the_actual_password) or spring.datasource.url = #{SPRING_DATASOURCE_URL:#{'the_actual_url'}} which is totally not what I need and it's pretty useless if you ask me. I mean I could achieve the same thing writing spring.datasource.password = the_actual_password. The whole point is to hide the sensitive data..
Now, this being said I'll leave 2 screenshots with what I have. The reason why I am trying to achieve this is because when I'll push to GitHub I won't have to worry that my credentials or anything will be out in the open for everyone to see.
Here is how my environment variables look like:
Here is how application.properties look like:
Thank you in advance! And in case you have an answer to my question could you also let me know how can I achieve the same thing with environment variable but for the application.jwt properties?

Ok, the solution was pretty... stupid, I suppose, but here it is. The reason why it didn't worked is because the environment variables were defined after IntelliJ first opened as described here. It seems like everything is working fine now.
I managed to achieve the functionality that I wanted.
In other words this:
spring.datasource.url = ${SPRING_DATASOURCE_URL}
spring.datasource.username = ${SPRING_DATASOURCE_USERNAME}
spring.datasource.password = ${SPRING_DATASOURCE_PASSWORD}
spring.datasource.driver-class-name = ${SPRING_DATASOURCE_DRIVERCLASSNAME}
Is now working perfectly.
Thank you for all your help!

Have you used the correct annotations?
Given Properties as such:
root.test = ${TEST.VAR:-Test}
Environment variable like:
TEST.VAR = Something
Configuration class:
#Configuration
#ConfigurationProperties(prefix = "root")
public class Test {
private String test;
public String getTest() {
return test;
}
public void setTest(String test) {
this.test = test;
}
}
You can use Records by setting #ConfigurationPropertiesScan on your Main class
#SpringBootApplication
#ConfigurationPropertiesScan
public class DemoApplication{...}
Defining Record like so:
#ConfigurationProperties(prefix = "root")
public record Test (String test) {
}

Related

Cant find bean that is clearly defined

I have this configuration class in two different kotlin projects.
In one it works just fine. In the other one, I get an error and I am asked to make the class open, and when I run the program, the bean cannot be found.
I am really confused as the same configuration seems to be working fine in a different project. Could you please suggest a way that would allow me to avoid making the class open, as I dont see the reason for doing so since in the other project I did not have to, or a reason why the bean cannot be found?
#Configuration
#ComponentScan(value = ["com.bank.manager"])
#PropertySources(PropertySource("classpath:application.yml"),
PropertySource(value = ["file:\${bank.target.config}"]))
class BankInstanceConfig(#Autowired private val env: Environment) {
#Bean fun bankInstance(): List<String> {
return env.getRequiredProperty("bank-instance").toString().split("#")
}
}
Then on class level I define the bean like this:
#Service
class BankDiscoveryServiceImpl(#Autowired bankInstance: BankInstanceConfig) {}

What´s a good practice for accessing external configurations in Spring Boot?

I´m setting up a Spring Boot application where certain configurations are being read from my application.yaml-file. I´ve done this a few times before and it works well, but I wondered whether there is a better way to access this configuration during runtime or whether I´m creating possible issues by not following some best practice.
Right now the class that extracts the configuration is simply defined as a Component like this:
#Component
#EnableConfigurationProperties
#ConfigurationProperties("myPrefix")
public class MyExternalConfiguration{
private HashMap<String, Boolean> entries= new HashMap<String, Boolean>();
public Boolean getConfigurationForKey(String key) {
return this.entries.get(key);
}
}
And then autowired to several other classes that need to access this configuration like this:
#Component
public class MyClass{
#Autowired
private MyExternalConfiguration myExternalConfiguration;
public void doSomething(){
//...
Boolean someEntry = myExternalConfiguration.getConfigurationForKey(someKey);
}
}
Now, this does work just fine. It´s just that I have seen examples of where configurations like this are handled as a singleton for example (although not in a Spring-Boot environment). I would just like to ask, whether there is some commonly accepted way to access external configurations or whether you see an issue with the way i access it in my project.
Thank you in advance!
There is a whole chapter about configuration in the Spring Boot Reference Manual:
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-external-config
Simply said there are two options to access configuration:
With the Value annotation:
#Value("${name}")
private String name;
Or typesafe with a configuration class:
#ConfigurationProperties(prefix="my")
public class Config {
private List<String> servers = new ArrayList<String>();
public List<String> getServers() {
return this.servers;
}
}
So there is no need to read the configuration file by your own.

setServletContext not activating inside #Configuration file

So, this is something of a follow-on of this question. My current code looks something like this:
#Configuration
#EnableAutoConfiguration
#ComponentScan(basePackages = {"base.pkg.name"})
public class MyApp implements ServletContextAware {
private ThingDAO beanThingDAO = null;
public MyApp() {
// Lots of stuff goes here.
// no reference to servletContext, though
// beanThing gets initialized, and mostly populated.
}
#Bean publicD ThingDAO getBeanThingDAO() { return beanThingDAO; }
public void setServletContext(ServletContext servletContext) {
// all references to servletContext go here, including the
// bit where we call the appropriate setters in beanThingDAO
{
}
The problem is, it's not working. Specifically, my understanding was that setServletContext was supposed to be called by various forms of Spring Magic at some point during the startup process, but (as revealed by System.out.println()) it never gets called. I'm trying to finish up the first stage of a major bunch of refactoring, and for the moment it is of notable value to me to be able to handle the access to servletContext entirely inside of the #Configuration file. I'm not looking for an answer that will tell me that I should put it in the controllers. I'm looking for an answer that will either tell me how to get it working inside of the #Configuration file, or explain why that won't work, and what I can do about it.
I just ran into a very similar issue and while I'm not positive it's exactly the same problem I thought I'd record my solution in case it's helpful to others.
In my case I had a single #Configuration class for my spring boot application that implemented both ServletContextAware and WebMvcConfigurer.
In the end it turns out that Spring Boot has a bug (or at least undocumented restraint) that ServletContextAware.setServletContext() will never be called if you also implement WebMvcConfigurer on the same class. The solution was simply to split out a separate #Configuration class to implement ServletContextAware.
Here's a simple project I found that demonstrates and explains more what the problem was for me:
https://github.com/dvntucker/boot-issue-sample
The OP doesn't show that the bean in question was implementing both of these, but given the OP is using simplified example code I thought maybe the fact that the asker could have been implementing both interfaces in his actual code and might have omitted the second interface.
Well, I have an answer. It's not one I'm particularly happy with, so I won't be accepting it, but if someone with my same problem stumbles across this question, I want to at least give them the benefit of my experience.
For some reason, the ServletContextAware automatic call simply doesn't work under those circumstances. It works for pretty much every other component, though. I created a kludge class that looks something like this:
// This class's only purpose is to act as a kludge to in some way get
// around the fact that ServletContextAware doesn't seem to work on MyApp.
// none of the *other* spring boot ways of getting the servlet context into a
// file seem to work either.
#Component
public class ServletContextSetter implements ServletContextAware {
private MyApp app;
public ServletContextSetter(MyApp app) {
this.app = app;
}
#Override
public void setServletContext(ServletContext servletContext) {
app.setServletContext(servletContext);
}
}
Does the job. I don't like it, and I will be rebuilding things later to make it unnecessary so I can take it out, but it does work. I'm going to hold the checkmark, though, in case anyone can tell me either how to make it work entirely inside the #Configuration - decorated file, or why it doesn't work there.
Note that the #Component decorator is important, here. Won't work without it.

More than one kind of profile with spring

I work with spring profiles with several configurations (prod, dev, test ...). I'm using several property files such as "application-prod.properties", "application-dev.properties ...). At this point, everything's fine.
But now, I want to add a language management using the same system. I tried to add 2 languages, english and french. So I created 2 more property files : "application-fr.properties" and "application-en.properties". Then I tag main main language configuration class with :
#Profile({ "fr", "en"})
public class MyClass{
public static String MYVAR;
#Value("${myclass.myvar}")
private void setMyVar(String myVar) {
MYVAR = myVar;
}
}
With my config files being like :
myclass.myvar=...
My active dev profile is "dev,en" for example, and it doesn't set my vars.
Any idea how to fix my issue ?
I fixed my issue, my syntax was ok, it was an ordering problem with my class constructions. My config file was initialized too late.

Overriding systemProperties in Spring?

I have a bit of code that looks like this:
#Value("#{systemProperties['TARGET_ENVIRONMENT']?: 'qa'}")
private String environment;
In my integration tests, this property is always blank, and I can't seem to do anything to give it definition. I tried doing something like this in my integration tests to simply override the target environment attribute to something else:
#Before
public void setUp(){
System.setProperty("TARGET_ENVIRONMENT", "I love dogs");
}
But that didn't really work out.
#Before
public void setUp(){
System.setProperty("TARGET_ENVIRONMENT", "qa"); -> Not I love Dogs
}
#Value("$peopleSearch{key}") -> Defined as per properties file
private String peopleSearch;
#Value("$peopleSearch{address}") -> Defined as per properties file
private String address;
But that doesn't really change anything. I have other properties (as above) which are being defined in properties files that work out ok and get values, but this one seems to be using the systemProperties attribute, which I have no idea how/where to modify. What do I do to override systemProperties attributes?
systemProperties isn't a function but variable. And it's initialized at the time Spring context is loaded. That's why setting system properties in runtime doesn't effect in your tests. Here you can find more details of SpringEL.
You can pass the system property -DTARGET_ENVIRONMENT to the runner of the test. For example, if you run the test in an IDE (Eclipse or IntelliJ), you can pass it under JVM arguments of the Run Configurations. Or if you run it by Maven, see this.

Categories