Spring Boot #Value Properties - spring

I have a Spring Boot application and in one of the classes, I try to reference a property from the application.properties file using #Value. But, the property does not get resolved. I have looked at similar posts and tried following the suggestions, but that didn't help. The class is:
#Configuration
#ComponentScan
#EnableAutoConfiguration
public class PrintProperty {
#Value("${file.directory}")
private String fileDirectory;
public void print() {
System.out.println(fileDirectory);
}
}
I have the property file.directory in application.properties. I have other fields as well.

I had the same problem like you. Here's my error code.
#Component
public class GetExprsAndEnvId {
#Value("hello")
private String Mysecret;
public GetExprsAndEnvId() {
System.out.println("construct");
}
public void print(){
System.out.println(this.Mysecret);
}
public String getMysecret() {
return Mysecret;
}
public void setMysecret(String mysecret) {
Mysecret = mysecret;
}
}
This is no problem like this, but
we need to use it like this:
#Autowired
private GetExprsAndEnvId getExprsAndEnvId;
not like this:
getExprsAndEnvId = new GetExprsAndEnvId();
Here, the field annotated with #Value is null because Spring doesn't know about the copy of GetExprsAndEnvId that is created with new and didn't know to how to inject values in it.

Make sure your application.properties file is under src/main/resources/application.properties. Is one way to go. Then add #PostConstruct as follows
Sample Application.properties
file.directory = somePlaceOverHere
Sample Java Class
#ComponentScan
public class PrintProperty {
#Value("${file.directory}")
private String fileDirectory;
#PostConstruct
public void print() {
System.out.println(fileDirectory);
}
}
Code above will print out "somePlaceOverhere"

I´d like to mention, that I used spring boot version 1.4.0 and since this version you can only write:
#Component
public class MongoConnection {
#Value("${spring.data.mongodb.host}")
private String mongoHost;
#Value("${spring.data.mongodb.port}")
private int mongoPort;
#Value("${spring.data.mongodb.database}")
private String mongoDB;
}
Then inject class whenever you want.
EDIT:
From nowadays I would use #ConfigurationProperties because you are able to inject property values in your POJOs. Keep hierarchical sort above your properties. Moreover, you can put validations above POJOs attributes and so on. Take a look at the link

To read the values from application.properties we need to just annotate our main class with #SpringBootApplication and the class where you are reading with #Component or variety of it. Below is the sample where I have read the values from application.properties and it is working fine when web service is invoked. If you deploy the same code as is and try to access from http://localhost:8080/hello you will get the value you have stored in application.properties for the key message.
package com.example;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#SpringBootApplication
#RestController
public class DemoApplication {
#Value("${message}")
private String message;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
#RequestMapping("/hello")
String home() {
return message;
}
}
Try and let me know

You haven't included package declarations in the OP but it is possible that neither #SpringBootApplication nor #ComponentScan are scanning for your #Component.
The #ComponentScan Javadoc states:
Either basePackageClasses or basePackages (or its alias value) may be
specified to define specific packages to scan. If specific packages
are not defined, scanning will occur from the package of the class
that declares this annotation.
ISTR wasting a lot of time on this before and found it easiest to simply move my application class to the highest package in my app's package tree.
More recently I encountered a gotcha were the property was being read before the value insertion had been done. Jesse's answer helped as #PostConstruct seems to be the earliest you can read the inserted values, and of course you should let Spring call this.

I had the similar issue and the above examples doesn't help me to read properties. I have posted the complete class which will help you to read properties values from application.properties file in SpringBoot application in the below link.
Spring Boot - Environment #Autowired throws NullPointerException

Your problem is that you need a static PropertySourcesPlaceholderConfigurer Bean definition in your configuration. I say static with emphasis, because I had a non-static one and it didn't work.
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}

I had the same issue get value for my property in my service class. I resolved it by using #ConfigurationProperties instead of #Value.
create a class like this:
import org.springframework.boot.context.properties.ConfigurationProperties;
#ConfigurationProperties(prefix = "file")
public class FileProperties {
private String directory;
public String getDirectory() {
return directory;
}
public void setDirectory(String dir) {
this.directory = dir;
}
}
add the following to your BootApplication class:
#EnableConfigurationProperties({
FileProperties.class
})
Inject FileProperties to your PrintProperty class, then you can get hold of the property through the getter method.

Related

Why setters are mandatory for fields in a class which reads properties from application.yml file in springboot?

Following is my code:
Why setter is mandatory. Without it, the class does not
read the property from the
application.yml file
correctly.
Thank you.
#Getter
#Setter
#NoArgsConstructor
#Configuration
#ConfigurationProperties(prefix = "test")
#EnableConfigurationProperties
public class KafkaTopicConfig {
private String bootstrapAddress;
#Value(value = "${test.bootstrapAddress}")
private String bootstrapAddressFromVariable;
should only use #Value in encapsulated components/services (we can call them configuration services).
This way, we will have all our configurations in one place, and that component will only have the responsibility of loading and providing them to other components.
https://stackabuse.com/the-value-annotation-in-spring
From baeldung.com... The Spring framework uses standard Java bean setters, so we must declare setters for each of the properties.
So it looks like you're using Lombok so I would make my class look more like this:
#ConfigurationProperties(prefix = "test")
#Data
public class KafkaTopicConfig {
private String bootstrapAddress;
}
Then in the main spring boot application class or a #Configuration class I would do:
#Configuration
#EnableConfigurationProperties({KafkaTopicConfig.class})
public MyApplicationConfig{
}
Then to use my configuration properties I would autowire it into the #Component where I wished to use it.
e.g.
#Component
public MyComponent{
private final KafkaTopicConfig config;
public MyComponent(KafkaTopicConfig config) {
this.config = config;
}
public void doStuff() {
if ("some address".equals(config.getBootstrapAddress())) {
blah();
}
}
}
Using the #Value inside the configuration properties feels confusing to me, and defeats the point of using configuration properties in the first place.

Using Spring boot, I am unable to refer a class present in another jar

I am trying to build a spring boot web application. I want to refer a class from another jar. The class name is SalaryHandler.
I have done the following configuration in the class having
#SpringBootApplication annotation:
#Bean
public SalaryHandler iSalary() {
return new SalaryHandler();
}
In the class, where it is required, I have used autowiring annotation like this:
package hello;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.salary.SalaryHandler;
//#Service - not working
//#Component - not working
public class SalaryDelegatorImpl implements SalaryDelegator {
#Autowired
private SalaryHandler iSalary;
#Override
public void show() {
iSalary.testSalary();
}
}
The code is compiling fine, but when this iSalary object is used to call its method, nullpointer exception is thrown.
Just to note that SalaryHandler is present inside another jar and is not using any spring annotation, its code is as below:
package com.salary;
public class SalaryHandler implements ISalary {
public void testSalary() {
System.out.println("Salary test successful...");
}
}
you need to attempt Autowire with #Component. In order to get this to work, you'll have to annotate a method in your #Configuration class. Something like this should allow you to autowire the class:
#Configuration
#ComponentScan("com.package.where.my.class.is")
public class ConfigClass{
#Bean
public JPADataService jpaDataService(){
return new JPADataService();
}
}
I am able to fix this. The problem was somewhere inside code, I was calling SalaryDelegatorImpl using new operator(from inside a factory class), so that was not being managed by Spring. As a result, the #Autowired on SalaryHandler, was not working.
I changed my factory to be spring managed, and then it worked fine.
Thanks everyone for the support.

Autowire working in unit test but not in main java class

I've a domain class that I want to auto-populate from external config. Here is my domain class:
#Data
#Configuration
#PropertySource("classpath:application.properties")
public class StudioVo {
#Value("${studio.code}")
private code;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Here is my context xml:
<bean class="org.springframework.batch.core.scope.StepScope" />
<bean id="ItemReader" class="com.sdm.studio.reader.StudioReader" scope="step">
<property name="studioVo" ref="StudioVo" />
</bean>
<bean id="StudioConfigVo" class="com.sdm.studio.domain.StudioVo" />
</bean>
Here is the class where I want to use the vo:
#Slf4j
#Data
public class StudioReader implements ItemReader<List<Studio>> {
private StudioVo studioVo;
public List<Studio> read() throws Exception {
System.out.println("getCode: " + studioVo.getCode()); //code is null here
return null;
}
}
However when I run it via unit test by autowiring, it runs fine. Like this:
#RunWith(SpringRunner.class)
#SpringBootTest
public class StudioTest {
#Autowired
private StudioVo studioVo;
#Test
public void testAutoPopulationOfStudio(){
System.out.println("getCode: "+ studioVo.getCode()); // works!
// Assert.assertTrue(studioVo.getCode().equals("102"));
}
}
Not sure what's going on here - I'm working with an old Spring Batch application wrapped in Spring Boot (so there is a mix of XML based and Java based config - and may be that is the cause of this issue). What am I missing?
In your StudioTest, you are autowiring StudioReader where as you missed the #Autowired in your StudioReader code, so add it as shown below:
#Slf4j
#Data
public class StudioReader implements ItemReader<List<Studio>> {
#Autowired //add this so that studioVo can be injected
private StudioVo studioVo;
//add other code
}
Please be certain to note that using #Autowire requires a chain of Spring-managed beans below it from wherever you are using it including the class in which you are using #Autowire. That is because Spring needs the precedent references to match up the object-reference hierarchy. I.e., in business logic layer ClassA, you want to #Autowire a field. ClassA itself needs to be a managed bean. Further, if the field you want to #Autowire holds an object that has referential dependencies to other objects (and most do), these also must be Spring-managed.
For example, the following will work:
package com.example.demo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MessageRunner {
private static SetterMessage setterMessage;
public static void main(String[] args) {
setterMessage = (SetterMessage) (new AnnotationConfigApplicationContext(DemoConfiguration.class)).getBean("setterMessage");
setterMessage.setMessage("Finally it works.");
p(setterMessage.getMessage());
}
private static void p(String s) {
System.out.println(s);
}
}
DemoConfiguration.java looks like this:
package com.example.demo;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan(basePackages = "com.example.demo")
public class DemoConfiguration {
}
SetterMessage.java, this:
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
#Service
#Scope("prototype")
public class SetterMessage {
private String message = null;
#Autowired
private SetterMessage2 setterMessage2;
public String getMessage(){
return message+setterMessage2.getSubMessage();
}
public void setMessage(String message) {
this.message = message;
setterMessage2.setSubMessage("("+message+")");
}
}
SetterMessage2.java:
package com.example.demo;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
#Service
#Scope("prototype")
public class SetterMessage2 {
private String subMsg = "";
public void setSubMessage(String msg) {
subMsg = msg;
}
public String getSubMessage() {
return subMsg;
}
}
Note that SetterMessage2.java is annotated as a Component (#Service) but no field in it is autowired. That is because it's the end of the object reference chain. But because it is a Component, it can be autowired into SetterMessage.java. However look at MessageRunner.java's main() method and field declarations. Note that the class field SetterMessage is NOT autowired. If it were annotated as #Autowired, main() would fail at runtime, throwing an NPE with the reference to setterMessage in main(). This is because MessageRunner.java is not marked as some kind of component. So we need to grab a valid instance of MessageSetter from the application context and use it.
To emphasize, the following version of MessageRunner.java's main() method WILL FAIL, throwing an NPE, if MessageRunner.java looked like this:
...
public class MessageRunner {
#Autowired // <-- This will not do the job for us
private static SetterMessage setterMessage;
public static void main(String[] args) {
setterMessage.setMessage("Finally it works."); // NPE here on ref to setterMessage
p(setterMessage.getMessage());
}
...
This is a real gotchya for people new to Spring. In fact, I'd place it among the Top Five Spring Newbie Discouragers and a really evil, pernicious detail that has caused new Spring programmers countless hours in aggravation and Google searches. So I do hope that noting this phenom here will save at least some newbies time and high blood pressure spikes.
Note: If you go to create the above classes in your IDE, bear in mind these were developed with Spring Boot enabled.

Spring #ConfigurationProperties not populated

I am experiencing problems using the #ConfigurationProperties feature.
Probably, I am missing something, since the mechanism seems very simple, but for me, it does not work.
I am using Spring Boot with the following main Application class
#SpringBootApplication
#EnableAspectJAutoProxy
#EnableConfigurationProperties(QueuesProperties.class)
#PropertySource("file:config/queues.properties")
#ImportResource("classpath:/spring-config.xml")
public class Application {
public static void main(String... args) {
ConfigurableApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
with QueuesProperties
#ConfigurationProperties(prefix = "wmq.in.queue")
public class QueuesProperties {
private static final Logger LOGGER = LoggerFactory.getLogger(QueuesProperties.class);
private String descr;
public String getDescr() {
return descr;
}
public void setDescr(String descr) {
this.descr = descr;
}
}
The properties file is very simple (I am trying to isolate the problem)
wmq.in.queue.descr = description
Then, I am trying to #Autowired the QueuesProperties in a #Component that I use in a spring-integration flow with a .
The QueuesProperties is correctly injected but the descr attribute is null.
#Autowired
private QueuesProperties queuesConfiguration;
while this
#Value("${wmq.in.queue.descr}")
private String descr;
is correctly evaluated.
I have made a lot of attempt with different configurations or code, but the result is the same. I get the QueuesProperties bean but it is not populated.
What am I missing?
Reading the question isn't very clear if the wmq.in.queue.descr = description properties is written in applciation.properties file. I said it because you say that the properties is correctly evaluated with #Value and not with
#Autowired
private QueuesProperties queuesConfiguration;
Even the #PropertySource("file:config/queues.properties") let me to think that probably the your wmq.in.queue.descr = description properties isn't written in applciation.properties but in file:config/queues.properties.
Summing
For use #ConfigurationProperties feature you have write the properties in application.properties and use #EnableConfigurationProperties(QueuesProperties.class) on #Component, #Configuration and so on annotated classes like below.
#Component
#EnableConfigurationProperties(QueuesProperties.class)
public class YourBean {
....
private final QueuesProperties queuesProperties;
public YourBean(QueuesProperties queuesProperties){
this.queuesProperties = queuesProperties;
}
.....
}
actually you can change the application.properties file name customizing spring boot properties evaluation but for your local app I discourage. I consider application.properties a good name for naming a place in which you put the configuration properties of your application
I hope that it can help you

How to inject java.nio.file.Path dependency using #ConfigurationProperties

I'm using Spring Boot and have the following Component class:
#Component
#ConfigurationProperties(prefix="file")
public class FileManager {
private Path localDirectory;
public void setLocalDirectory(File localDirectory) {
this.localDirectory = localDirectory.toPath();
}
...
}
And the following yaml properties file:
file:
localDirectory: /var/data/test
I would like to remove the reference of java.io.File (of setLocalDirectory) by replacing with java.nio.file.Path. However, I receive a binding error when I do this. Is there way to bind the property to a Path (e.g. by using annotations)?
To add to jst's answer, the Spring Boot annotation #ConfigurationPropertiesBinding can be used for Spring Boot to recognize the converter for property binding, as mentioned in the documentation under Properties Conversion:
#Component
#ConfigurationPropertiesBinding
public class StringToPathConverter implements Converter<String, Path> {
#Override
public Path convert(String pathAsString) {
return Paths.get(pathAsString);
}
}
I don't know if there is a way with annotations, but you could add a Converter to your app. Marking it as a #Component with #ComponentScan enabled works, but you may have to play around with getting it properly registered with the ConversionService otherwise.
#Component
public class PathConverter implements Converter<String,Path>{
#Override
public Path convert(String path) {
return Paths.get(path);
}
When Spring sees you want a Path but it has a String (from your application.properties), it will lookup in its registry and find it knows how to do it.
I took up james idea and defined the converter within the spring boot configuration:
#SpringBootConfiguration
public class Configuration {
public class PathConverter implements Converter<String, Path> {
#Override
public Path convert(String path) {
return Paths.get(path);
}
}
#Bean
#ConfigurationPropertiesBinding
public PathConverter getStringToPathConverter() {
return new PathConverter();
}
}

Resources