Spring Boot always shows #Value annotation's value to be null - java

I have a small class in my Spring-Boot project in which I am getting the some values from the application.properties file. My class is in src/main/java/com/company/package folder and my application properties file in src/main/resources/application.properties. This is my class:
#Component
#Configuration
#PropertySource("classpath:application.properties")
public class ElasticConnection {
private static final String INDEX = "index_name";
public static final String TYPE = "type";
private String user;
private String password;
public ElasticConnection(#Value("${elasticsearch.user}") String user,
#Value("${elasticsearch.password}") String password){
this.user = user;
this.password = password;
}
I am Autowiring this class to the Application class:
#Configuration
#SpringBootApplication
public class ElasticApplication {
#Autowired
ElasticConnection elastic;
public static void main(String[] args) {
SpringApplication.run(ElasticApplication.class, args);
}
}
Now as soon as the constrctor of the ElasticConnection class is called user and password are set to null. I know the values are read properly because InteliJ is kind enough to show the value (until you click on #Value and shows the "${elasticsearch.user}" again)
EDIT: this is the application properties file.
# App config
server.port=8070
# Local ElasticSearch instance configuration
elasticsearch.clustername = elasticsearch
elasticsearch.host = localhost
elasticsearch.port = 9300
#spring.main.allow-bean-definition-overriding=true
# Elasticsearch Settings
elasticsearch.user=test
elasticsearch.password=test

If we want to use the #Value annotation for a constructor argument we must not forget to add the #Autowired annotation on the constructor as well.So your code should be :
#Component
#PropertySource("classpath:application.properties")
public class ElasticConnection {
private static final String INDEX = "index_name";
public static final String TYPE = "type";
private String user;
private String password;
#Autowired
public ElasticConnection(#Value("${elasticsearch.user}") String user,
#Value("${elasticsearch.password}") String password){
this.user = user;
this.password = password;
}
This enables spring to use constructor injection for your fields.
Instead of injecting the values via the constructor you can also try the following in your code :
#Component
#PropertySource("classpath:application.properties")
public class ElasticConnection {
private static final String INDEX = "index_name";
public static final String TYPE = "type";
#Value("${elasticsearch.user}")
private String user;
#Value("${elasticsearch.password}")
private String password;
//setters and getters
}
Or you can even use #ConfigurationProperties annotation to read values from the property files.
Related : Spring #Autowire on Properties vs Constructor

Related

Spring: Could not resolve placeholder 'properties.string' in value '${properties.string}'

I'm getting an error when trying to run a Spring application where I am trying to inject dependencies from an application.properties file. Thus, Spring cannot create the bean.
This is the bean that cannot be created without the dependencies:
#Configuration
public class ClientConfig {
private String url;
private String id;
private String secret;
private String scope;
private String tenantId;
private String siteId;
private String listId;
#Autowired
public ClientConfig(
#Value("${client.url}") String url,
#Value("${client.id}")String id,
#Value("${client.secret}") String secret,
#Value("${client.scope}") String scope,
#Value("${client.tenantId}") String tenantId,
#Value("${client.siteId}") String siteId,
#Value("${client.listId}") String listId) {
this.url = url;
this.id = id;
this.secret = secret;
this.scope = scope;
this.tenantId = tenantId;
this.siteId = siteId;
this.listId = listId;
}
public ClientConfig() {
}
public String getUrl() {
return url;
}
public String getId() {
return id;
}
public String getSecret() {
return secret;
}
public String getScope() {
return scope;
}
public String getTenantId() {
return tenantId;
}
public String getSiteId() {
return siteId;
}
public String getListId() {
return listId;
}
}
These are the values in the application.properties file located in src/main/resources:
client:
url: https://google.com
id: ${CLIENT_ID}
secret: ${CLIENT_SECRET}
scope: https://scope.url.com
tenantId: 000-000-000
siteId: "google.com/site/mysite"
listId: "my-list"
The id and secret come from the environment.
I have tried a few different things. I have tried to use the #PropertySource annotation with the classpath:application.properties, but I get an error that the file doesn't exist. I tried moving those #Value tags outside the constructor. I have also tried to hard-code the values within the ClientConfig class. When I did that, I got an error that looked like this:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.example.demo.graph.GraphConfig required a bean of type 'java.lang.String' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'java.lang.String' in your configuration.
I'm new to Spring, but based on the training I have done, it seems like if the dependencies are hard-coded right within the class the application context would definitely be able to inject them. I know hard-coding them is not the answer so I'm not even worried about the second error.
Your file is named application.properties but its content you showed is in .yml-format.
In .properties-format your configuration would look like this:
client.url=https://google.com
client.id=${CLIENT_ID}
client.secret=${CLIENT_SECRET}
client.scope=https://scope.url.com
client.tenantId=000-000-000
client.siteId=google.com/site/mysite
client.listId=my-list
Alternatively you could use an application.yml file instead as described here: https://www.baeldung.com/spring-boot-yaml-vs-properties

Using #Value annotation to Inject single property

I have a class in which i want only one property to be injected from the properties file.
The class is like this:
#Getter
public class BatchContext {
private final String city;
private final String channel;
#Value("${table.url: https://www.someurl.com}")
private final String url;
#Builder
public BatchContext(String city, String channel, #Value("${table.url:https://www.someurl.com}") String url) {
this.city = city;
this.channel = channel;
this.url = url;
}
}
I wanted to just pass country and segment to the BatchContext and wanted to load url from the properties file but the url turns out to be null, whats the best way to achieve this.
Madu, it seems your problem is related just to your class.
It is not a Bean for Spring context, then your #Value cannot be injected by Spring.
Try setting your class as an object managed by Spring, e.g. #Component:
#Component
public class BatchContext {...
}

Best way to access multiple properties and assign dynamically in spring boot

what is the best way to represent the below properties in Spring for each category? Currently we have implemented for aaa properties. We always have two fields for each category.
We want to use the same POJO for all categories. Please suggest the best approach that fits.
Property file name package.properties
category.aaa.field1 = value1
category.aaa.field2 = value2
category.bbb.field1 = value1
category.bbb.field2 = value2
category.ccc.field1 = value1
category.ccc.field2 = value2
category.ddd.field1 = value1
category.ddd.field2 = value2
We have created a POJO with
#Data
public class CategoryPojo{
private String valueOne;
private String valueTwo;
}
we are reading it in #Configuration using #PropertySource
#Configuration
#ComponentScan(value = "com.category")
#PropertySource("classpath:package.properties")
public class CategoryConfig{
#Value("${category.aaa.field1}")
private String fieldOne;
#Value("${category.aaa.field2}")
private String fieldTwo;
#Bean(name = "getACategory")
public CategoryPojo getFields() {
return new CategoryPojo(fieldOne, fieldTwo);
}
}
You can, and should, use https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-typesafe-configuration-properties
To handle this.
#Data
#ConfigurationProperties(prefix="category")
public class MyProperties{
private CategoryPojo aaa;
private CategoryPojo bbb;
private CategoryPojo ccc;
private CategoryPojo ddd;
#Data
public static class CategoryPojo{
private String valueOne;
private String valueTwo;
}
}
You can then place #EnableConfigurationProperties(classes = MyProperties.class) on your main application class or another #Configuration and then #Autowire your MyProperties class where its needed.

Property annotated with #Value is always null

#Component
public class Bot extends TelegramLongPollingBot {
#Value("${camel.component.telegram.authorization-token}") //properties
private String botToken; //null
TelegramBot bot = TelegramBotAdapter.buildDebug(botToken);
.
.
.
}
I don't understand why is not injected a param value defined in properties.
Notice that #Value do not support relaxed binding, so check your property.
#Component
#PropertySource("file:${app_env_path}/${app_env}_DBconnection.properties")
public class DBProperties {
#Value("${driver.class.name}")
private String driverClassName;
#Value("${db.url}")
private String url;
#Value("${db.username}")
private String username;
#Value("${db.password}")
private String password;
#Value("${db.poolsize}")
private String poolsize;
//setters and getters
}
Property file -
driver.class.name=oracle.jdbc.OracleDriver
db.url=
db.username=
db.password=
db.poolsize=100
app_env_path = path where the location of the file
app_env = SIT/PROD

Spring: #Service class with constructor with #Autowired(required = false) parameters: how to initialize it with these parameters?

I have a service class that I want to dynamically initialize with different incoming values of constructor parameters:
#Service
public class SomeServiceImpl implements SomeService {
private final SomeProperties someProperties;
private final String url;
private final String password;
private final Logger log = LoggerFactory.getLogger(SomeServiceImpl.class);
#Autowired
public SomeServiceImpl(SomeProperties someProperties,
#Autowired(required = false) String url,
#Autowired(required = false) String password) {
this.someProperties = someProperties;
this.url = url;
this.password = password;
}
Is it possible to initialize during runtime this #Service inside another spring component class by using own supplied #Autowired(required = false) parameters (in this case own url and password)? How would this code look like?
You can do it like this
#Configuration
class SomeConfigClass {
#Autowired
SomeProperties someProperties
#Value("${url1}")
String url1
#Value("${password1}")
String password1
..............
// Do this for other url's and properties or check out #ConfigurationProperties
..............
#Bean("someService1")
public SomeService() {
return new SomeService(someProperties, url1, password1);
}
#Bean("someService2")
public SomeService() {
return new SomeService(someProperties, url2, password2);
}
...............
..............
}
Create a factory class
#Configuration //typo corrected
class SomeServiceFactory {
#Autowired // Spring will Autowire all instances of SomeService with bean name as key
Map<String, SomeService> someServiceMap;
public SomeService getSomeServiceByName(String name) {
return someServiceMap.get(name);
}
}
Then you can use the instance like this
#RestController
class SomeController {
#Autowired
SomeServiceFactory someServiceFactory;
public void someEndpoint() {
SomeService someService1 = SomeServiceFactory.getSomeServiceByName("someService1"); //You need to decide what argument to pass based on condition
someService1.someFunction(...); // this will have url1 and password1
}
}
Where user and password come from ?
Maybe you can simply remove them from constructor and use #Value annotation to read values from properties file ?
#Service
public class SomeServiceImpl implements SomeService {
private final SomeProperties someProperties;
#Value("${service.url}")
private String url;
#Value("${service.password}")
private String password;
private final Logger log = LoggerFactory.getLogger(SomeServiceImpl.class);
#Autowired
public SomeServiceImpl(SomeProperties someProperties) {
this.someProperties = someProperties;
}

Categories

Resources