Spring boot #Value not working for static field - java

I am trying to get value from a property file on spring boot.
application.properties file is under resources folder, and its content;
TEST=someText
And the code is;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
#SpringBootApplication
#PropertySource("classpath:application.properties")
public class Bb8Application {
#Value("${TEST}")
static String someString;
public static void main(String[] args) {
System.out.print(someString);
}
}
I get NULL as a result instead of "someText". Is there something that I am missing?

Spring does not allow injecting to static fields. If you really want to use static variable you can try this workaround.

You can use #Value annotation for static fields.
#Component
public class MyClass {
private static String MY_STATIC_FIELD;
public MyClass(#Value("${TEST}") String input) {
MY_STATIC_FIELD = input;
}
}
more for information: https://www.baeldung.com/spring-inject-static-field

Related

Spring : Cannot get value from application.properties

I created Spring project via Spring Initializr with project following struct:
I defined property in application.properties file :
my.prop=testvalue
I inject this value into MyClass as following :
#Component
class MyClass {
#Value("${my.prop}")
private String myProp;
public String getMyProp() {
return myProp;
}
}
ConfigBeans defined as following:
package com.example.propertiesdemo;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
#Configuration
public class ConfigBeans {
#Bean
public MyClass myLitoBean() {
return new MyClass();
}
}
PropertiesdemoApplication.java :
package com.example.propertiesdemo;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
#SpringBootApplication
public class PropertiesdemoApplication {
public static void main(String[] args) {
ApplicationContext context
= new AnnotationConfigApplicationContext(
ConfigBeans.class);
MyClass myClass = context.getBean(MyClass.class);
System.out.println(myClass.getMyProp());
}
}
I am expecting that after executing line
System.out.println(myClass.getMyProp());
will be printed value of myprop defined in application.properties (i.e testvalue), but after running (via Netbeans IDE) I get output :
${my.prop}
What was missed / wromg in this code ? Thanks in advance
You are creating MyClass bean twice.
Using #component annotation
using #bean annotation in the config class (use method name lowerCamelCase i.e. in your case myClass())
Create bean only once using any one of the above.
You dont need to create an application context in the main method like this. The presented code is a kind of mixture of "traditional" spring and spring boot. So you're kind of bypassing all the goodies that spring boot offers, among which is automatic application.properties loading.
If you're using spring boot (there is a #SpringBootApplication annotation) then it will create everything by itself.
Usually it should be something like this
public static void main(String[] args) {
SpringApplication.run(PropertiesdemoApplication.class, args);
}
Right, as Navnath Adsul said, you need the bean to be created once, and also, since you are using Spring Boot, you need to raise the context using a special method
#SpringBootApplication
public class PropertiesdemoApplication implements CommandLineRunner {
// Inject Bean
#Autowired
private MyClass myClass;
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(PropertiesdemoApplication.class)
.run(args);
// or SpringApplication.run(PropertiesdemoApplication.class, args);
}
#Override
public void run(String[] args) throws Exception {
System.out.println(myClass.getMyProp());
}
}
#SpringBootApplication
public class PropertiesdemoApplication {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(PropertiesdemoApplication.class, args);
MyClass myClass = context.getBean(MyClass.class);
System.out.println(myClass.getMyProp());
}
}

Spring autowire of static configuration-object fails

as a Spring newbie I try to load external configuration into a Configuration-Object which I then try to autowire.
Following is my setup broken down to a bare minimum (less fields aso):
Package-structure:
app
|--Application.java
app.configuration
|--ConfigProperties.java
With my configuration.properties residing in:
src/main/resources/config
Application.java:
package app;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import app.configuration.ConfigProperties;
#SpringBootApplication
public class Application
{
//just to test if autowire works
#Autowired
public static ConfigProperties config;
public static void main(String[] args)
{
SpringApplication.run(Application.class, args);
System.out.println(config.getId());
}
}
ConfigProperties.java:
package app.configuration;
import javax.validation.constraints.NotBlank;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.validation.annotation.Validated;
#Configuration
#PropertySource("classpath:/config/configuration.properties")
#ConfigurationProperties
#Validated
public class ConfigProperties
{
#NotBlank
private String id;
public String getId()
{
return id;
}
public void setId(String id)
{
this.id = id;
}
}
This results in a NullPointerException for the line:
System.out.println(config.getId());
in the Apllication.java.
I read several posts on this topic but most imply that own instantiation prevents autowiring, which I am not doing, or that the scan can't find the class to autowire, but from what I read with my annotations and package structure that should not be the problem.
Where is/are my mistake(s)?
PS: it seems that properties are read correctly from the property file. If I test the validation, the expected error is thrown for Null-value if leave out the part where I try to autowire.
#Autowired
public static ConfigProperties config;
You cannot autowire static fields in Spring.
//just to test if autowire works
You can write a new Junit test cases for that.
Something like:
#Autowired
public ConfigProperties config;
in your test class.
Since the annotation #Autowired requires getters/setters to inject the correct implementation, I don't suggest you to wire a static content. However, there is a workaround for this issue:
public static ConfigProperties config;
#Autowired
public Application(ConfigProperties config) {
Application.config= config;
}
Alternatively, take a look on the #PostConstruct annotation.
Do not use a static ConfigProperties . Then use #PostConstruct to print the ID from your config object.
#Autowired
ConfigProperties config;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#PostConstruct
void printId(){
System.out.println("ID = " + config.getId());
}

Configuration properties are not loaded in Spring Test

I have a Spring Boot application which has some configuration properties. I'm trying to write a test for some of the components and want to load configuration properties from a test.properties file. I can't get it to work.
Here's my code:
test.properties file (under src/test/resources):
vehicleSequence.propagationTreeMaxSize=10000
Configuration properties class:
package com.acme.foo.vehiclesequence.config;
import javax.validation.constraints.NotNull;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
#Component
#ConfigurationProperties(prefix = VehicleSequenceConfigurationProperties.PREFIX)
public class VehicleSequenceConfigurationProperties {
static final String PREFIX = "vehicleSequence";
#NotNull
private Integer propagationTreeMaxSize;
public Integer getPropagationTreeMaxSize() {
return propagationTreeMaxSize;
}
public void setPropagationTreeMaxSize(Integer propagationTreeMaxSize) {
this.propagationTreeMaxSize = propagationTreeMaxSize;
}
}
My test:
#RunWith(SpringRunner.class)
#ContextConfiguration(classes = VehicleSequenceConfigurationProperties.class)
#TestPropertySource("/test.properties")
public class VehicleSequenceConfigurationPropertiesTest {
#Autowired
private VehicleSequenceConfigurationProperties vehicleSequenceConfigurationProperties;
#Test
public void checkPropagationTreeMaxSize() {
assertThat(vehicleSequenceConfigurationProperties.getPropagationTreeMaxSize()).isEqualTo(10000);
}
}
The test fails with "Expecting actual not to be null" meaning the property propagationTreeMaxSize in the configuration properties class was not set.
Two minutes after posting the question, I've found the answer.
I had to enable configuration properties with #EnableConfigurationProperties(VehicleSequenceConfigurationProperties.class):
#RunWith(SpringRunner.class)
#TestPropertySource("/test.properties")
#EnableConfigurationProperties(VehicleSequenceConfigurationProperties.class)
public class VehicleSequenceConfigurationPropertiesTest {
#Autowired
private VehicleSequenceConfigurationProperties vehicleSequenceConfigurationProperties;
#Test
public void checkPropagationTreeMaxSize() {
assertThat(vehicleSequenceConfigurationProperties.getPropagationTreeMaxSize()).isEqualTo(10000);
}
}

How to load #Value annotation in the main class?

#SpringBootApplication
public class App {
#Value("${Application}")
private String application;
#Value("${APP_SERVER_CPU_ENABLED}")
private String APP_SERVER_CPU_ENABLED;
#Value("${APP_SERVER_MEMORY_ENABLED}")
private String APP_SERVER_MEMORY_ENABLED;
#Autowired
MonitoringItems mI;
public static void main(String[] args) {
SpringApplication.run(App.class, args);
MonitoringItems mI=null;
try {
System.out.println(APP_SERVER_CPU_ENABLED);
System.out.println(APP_SERVER_MEMORY_ENABLED);
if (APP_SERVER_MEMORY_ENABLED.equalsIgnoreCase("true") && APP_SERVER_CPU_ENABLED.equalsIgnoreCase("true")) {
//do something
}
How come the main class cannot read the #Value annotations? In my other classes I have to put #Configuration, #ComponentScan, #EnableAutoConfiguration above the class and then #PostConstruct above the constructor. And I was able to retrieve the values from the application.properties. How would I be able to do that for the main class?
The problem is that you cannot make your #Values static, because Spring may not have initialized them yet. You can however get the value you want BEFORE spring is initialized by retrieving them from the system/environment variables instead of via a properties file.
String value = System.getProperty("SOME_KEY");
So after 3 years i show you an astuce to do that.
#SpringBootApplication
public class Demo1Application {
public static void main(String[] args) {
ApplicationContext context = SpringApplication.run(Demo1Application .class, args);
Test test = context.getBean(Test.class);
String password = test.getPassword();
System.out.println(password);
}
#Component
class Test {
#Value("${password}")
private String password;
public String getPassword() {
return password;
}
}
}
of course you create your property in a file properties like application.properties for example :
password = sxqdqdqdqdqdqd

How to use YamlPropertiesFactoryBean to load YAML files using Spring Framework 4.1?

I have a spring application that is currently using *.properties files and I want to have it using YAML files instead.
I found the class YamlPropertiesFactoryBean that seems to be capable of doing what I need.
My problem is that I'm not sure how to use this class in my Spring application (which is using annotation based configuration).
It seems I should configure it in the PropertySourcesPlaceholderConfigurer with the setBeanFactory method.
Previously I was loading property files using #PropertySource as follows:
#Configuration
#PropertySource("classpath:/default.properties")
public class PropertiesConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
How can I enable the YamlPropertiesFactoryBean in the PropertySourcesPlaceholderConfigurer so that I can load YAML files directly?
Or is there another way of doing this?
Thanks.
My application is using annotation based config and I'm using Spring Framework 4.1.4.
I found some information but it always pointed me to Spring Boot, like this one.
With XML config I've been using this construct:
<context:annotation-config/>
<bean id="yamlProperties" class="org.springframework.beans.factory.config.YamlPropertiesFactoryBean">
<property name="resources" value="classpath:test.yml"/>
</bean>
<context:property-placeholder properties-ref="yamlProperties"/>
Of course you have to have the snakeyaml dependency on your runtime classpath.
I prefer XML config over the java config, but I recon it shouldn't be hard to convert it.
edit:
java config for completeness sake
#Bean
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer = new PropertySourcesPlaceholderConfigurer();
YamlPropertiesFactoryBean yaml = new YamlPropertiesFactoryBean();
yaml.setResources(new ClassPathResource("default.yml"));
propertySourcesPlaceholderConfigurer.setProperties(yaml.getObject());
return propertySourcesPlaceholderConfigurer;
}
To read .yml file in Spring you can use next approach.
For example you have this .yml file:
section1:
key1: "value1"
key2: "value2"
section2:
key1: "value1"
key2: "value2"
Then define 2 Java POJOs:
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "section1")
public class MyCustomSection1 {
private String key1;
private String key2;
// define setters and getters.
}
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "section2")
public class MyCustomSection1 {
private String key1;
private String key2;
// define setters and getters.
}
Now you can autowire these beans in your component. For example:
#Component
public class MyPropertiesAggregator {
#Autowired
private MyCustomSection1 section;
}
In case you are using Spring Boot everything will be auto scaned and instantiated:
#SpringBootApplication
public class MainBootApplication {
public static void main(String[] args) {
new SpringApplicationBuilder()
.sources(MainBootApplication.class)
.bannerMode(OFF)
.run(args);
}
}
If you'are using JUnit there is a basic test setup for loading YAML file:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(MainBootApplication.class)
public class MyJUnitTests {
...
}
If you're using TestNG there is a sample of test configuration:
#SpringApplicationConfiguration(MainBootApplication.class)
public abstract class BaseITTest extends AbstractTestNGSpringContextTests {
....
}
`
package com.yaml.yamlsample;
import com.yaml.yamlsample.config.factory.YamlPropertySourceFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.PropertySource;
#SpringBootApplication
#PropertySource(value = "classpath:My-Yaml-Example-File.yml", factory = YamlPropertySourceFactory.class)
public class YamlSampleApplication implements CommandLineRunner {
public static void main(String[] args) {
SpringApplication.run(YamlSampleApplication.class, args);
}
#Value("${person.firstName}")
private String firstName;
#Override
public void run(String... args) throws Exception {
System.out.println("first Name :" + firstName);
}
}
package com.yaml.yamlsample.config.factory;
import org.springframework.boot.env.YamlPropertySourceLoader;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.EncodedResource;
import java.io.IOException;
import java.util.List;
public class YamlPropertySourceFactory extends DefaultPropertySourceFactory {
#Override
public PropertySource createPropertySource(String name, EncodedResource resource) throws IOException {
if (resource == null) {
return super.createPropertySource(name, resource);
}
List<PropertySource<?>> propertySourceList = new YamlPropertySourceLoader().load(resource.getResource().getFilename(), resource.getResource());
if (!propertySourceList.isEmpty()) {
return propertySourceList.iterator().next();
}
return super.createPropertySource(name, resource);
}
}
My-Yaml-Example-File.yml
person:
firstName: Mahmoud
middleName:Ahmed
Reference my example on github spring-boot-yaml-sample So you can load yaml files and inject values using #Value()
I spend 5 to 6 hours in understanding why external configuration of yml/yaml file(not application.yml) are so different.I read various articles, stack overflow questions but didn't get correct answer.
I was stuck in between like I was able to use custom yml file value using YamlPropertySourceLoader but not able to use #Value because it is giving me error like
Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder
'fullname.firstname' in value "${fullname.firstname}"
fullname is a property in yml.
Then I used "turtlesallthewaydown" given above solution, then at last I was able to use #Value without any issues for yaml files and I removed YamlPropertySourceLoader.
Now I understand the difference between YamlPropertySourceLoader and PropertySourcesPlaceholderConfigurer, a big thanks, however I have added these changes in my git repo.
Git Repo:
https://github.com/Atishay007/spring-boot-with-restful-web-services
Class Name: SpringMicroservicesApplication.java
If one will seek how to load yaml file into Properties in Spring, then there is a solution:
public Properties loadYaml(String fileName){
// fileName for eg is "my-settings.yaml"
YamlPropertySourceLoader ypsl = new YamlPropertySourceLoader();
PropertySource ps = ypsl.load(fileName, new ClassPathResource(fileName)).get(0);
Properties props = new Properties();
props.putAll((Map)ps.getSource());
return props;
}

Categories