Using #Value annotation to Inject single property - java

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 {...
}

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

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

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

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 class RequestBody and MongoRepository

I have class:
class TestClass {
#Id
private ObjectId id;
private ObjectId parentId;
private String name;
private String describe;
private String privateData;
public TestClass(ObjectId parentId, String name, String describe, String privateData) {
this.parrentId = parrentId;
this.name = name;
this.describe = describe;
this.privateDate = privateData;
}
// get/set methods...
}
Can I use this class in MongoRepository and #RequestBody? Is it safe? parrentId and privateData is private properties and RequestBody does not have to fill them.
mongorepository:
public interface TestClassRepository extends MongoRepository<TestClass, String> {
public TestClass findById(ObjectId id);
}
post method:
#RequestMapping(value="/testclass", method=RequestMethod.POST)
public void create(#RequestBody TestClass testClass) {
testClass.setParentId(...);
repo.insert(testClass);
}
For example:
{"name": "test", "describe": "test", "id": "54d5261a8314fe3c650d5b1d", "parentId": "54d5261a8314fe3c650d5b1d", "privateData": "WrongPrivateData"}
How can I do that it was impossible to set properties id, parentId, privateDate?
Or need I create new class for RequestBody? I don't want duplicate code.
It should be better and safe to use separate models for DAO and VO layers(view). If your models currently looks the same, it doesn't mean that they will stay the same in future. You can use the Dozer Mapping framework for mappings between your models. It's easy,fast and safe.
If you need to skip some field from mongotemplate mapping use #Transient annotation.
P.S. You don't need findById method, because mongotemplate already have find method which uses key as param. TestClass should have an empty constructor.

Categories

Resources