when I have values in a property file and read them in like
#Value(${ftpserver.maxconnections})
private float maxConnections;
It works and spring auto parse a value if it is 0f.
However there is no f,l,d for integer but I need an int.
If I just write the number without any postfix Spring complains that it cannot parse a java.lang.String to int.
According to various Internet sources it should work, but it does not.
I set the file like
#PropertySource("classpath:application.properties")
and in the same configuration file
#Bean
public static PropertySourcesPlaceholderConfigurer pspc() {
return new PropertySourcesPlaceholderConfigurer();
}
To recap I want this to work
#Value(${ftpserver.maxconnections})
private float maxConnections;
and not get an Exception for cannot parse String to int from the Spring ContextLoader.
You need to define a ConversionService :
#Bean
public ConversionService conversionService() {
return new DefaultConversionService();
}
You can't define this conversionService() in the same class as the one using this #Value (because the converter will not be initalized)
You can parse the Object in the properties file to an int using this:
#Value("#{T(java.lang.Integer).parseInt('${property.name:{number}}')}")
Related
If I have some global config properties value that want to set on application start up, one of the ways to do is by setting it in application.properties and then using #Value to inject those values. However, if I want to set the values by making an API call to get those properties value on application start up and then set the values (but want to use similar way as #Value), rather than getting and setting it via properties files, how should it be achieved ?
#Configuration
public class config {
#Value("${properties1}")
private String properties1;
#Value("${properties2}")
private String properties2;
}
I have done some web search on custom property source (https://projects.spring.io/spring-cloud/spring-cloud.html#customizing-bootstrap-property-sources), and tried to follow the example, but encountered the error that the placeholder could not be resolved. How to get back the value ?
Could not resolve placeholder 'property.from.sample.custom.source' in value "${property.from.sample.custom.source}"
#Configuration
public class CustomPropertySourceLocator implements PropertySourceLocator {
#Override
public PropertySource<?> locate(Environment environment) {
return new MapPropertySource("customProperty",
Collections.<String, Object>singletonMap("property.from.sample.custom.source", "worked as intended"));
}
}
#Service
public class MainService {
#Value("${property.from.sample.custom.source}")
private String value;
public void printValue() {
System.out.println("value - " + value);
}
}
Assuming you use Spring Boot, you can run the Spring Boot application and pass the arguments using the following Maven command (depends on the Spring Boot version):
Spring Boot 1.x: using -Drun.arguments:
spring-boot:run -Drun.arguments=--properties1=One,--properties2=Two
Spring Boot 2.x: using -Dspring-boot.run.arguments:
spring-boot:run -Dspring-boot.run.arguments=--properties1=One,--properties2=Two
Now you can access the values using the #Value annotation:
#Value("${properties1}")
private String properties1;
#Value("${properties2}")
private String properties2;
Note: Once the properties are defined in the properties files (ex. application.properties and/or application-dev.yml etc..), defined in the command line like above, they can be accessed through the #Value annotation.
Baeldung's website offers a nice article: Command-Line Arguments in Spring Boot.
I am not sure if we can set values to properties file after application is up. Because properties file will be injected to #Bean when application is running. But we can hack this.
First, Create a file that contains all the configuration file which will be load from properties file. This file will be our template and initial / default values of the configuration.
Let say we have below configuration as application.yml
config:
name: Anna
age: 18
Then create configuration file
#ConfigurationProperties(prefix = "config")
public class ConfigProperties {
private String name;
private Integer age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
Don't forget to modify our Application class
#SpringBootApplication
#EnableConfigurationProperties(ConfigProperties::class)
public class Application { ... }
Through here, we can just #Autowire the config class to get the value we want, rather than using a #Value.
Through here we already have the properties value to our config class. Then if you want to update the properties value (now already on config file) through an API, do a simple setName(...).
But, we have to be aware that this way is only work on a single run. When we restart the application, the value will use the default from properties file.
I'm using #Value to inject parameters from my properties file to variable in my application.
Because I'm not the only user in the application and I want to make the injection safety I need to validate the parameter before the injection.
Example:
properties file:
example.string=str1,str2
app.java
#Value(${example.string})
public String example;
the expected behavior in this case for example is to throw an exception because I assume "," id delimiter in array case
I don't think you can directly with #Value. But you could do something like this, which will fail on startup if validation fails:
#Validated
#ConfigurationProperties(prefix="my.prefix")
public class AppProperties {
//Validation annotations here
//#NotEmpty
//#MyCustomValidation
private String exampleString;
// getters / setters
}
I don't think you can do this before the injection, try to use the post construct method
you can do some thing like that :
#PostConstruct
public void validateValue() {
if (someProperty.contains(",")) {
throw new MyException("error");
}
}
I have application.properties file:
value=a
Then I would like to load a property file based on that value - a.properties and read/use properties from that file.
I was thinking about something like this:
#Configuration
public class PropertiesConfiguration {
#Value("${value}")
private String value;
#Bean
public PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
configurer.setLocation(new ClassPathResource(value + ".properties"));
return configurer;
} }
but the value is always null for some reason. When I try to get that value e.g. in service/component then it works fine. I would like to avoid using spring profiles. Any ideas how to achieve that with latest Spring?
In the line configurer.setLocation(new ClassPathResource(value + ".properties")); it should be "application.properties" since your file name is application.properties
Also in the properties file define it as
value = a
with spaces
One of the solutions I have come across and works fine is use of Component and PropertySource annotations.
#Component
#PropertySource(value = "classpath:${value}.properties")
public class CountryService implements ICountryService {
#Value("${<whatever in a.properties file>}")
private String currency;
#Override
public String getCurrency() {
return currency;
}
}
where ${value} is taken from application.properties. Then Autowire that bean whenever needed.
This may be silly question to ask but i'm unable to find any satisfactory solution to my problem. In java we don't have the concept of default variables so i am trying to give default value from properties file to my function parameters/arguments using #Value annotation, but i'm always getting null and i'm unable to figure why is this happening. Please help me to solve the issue or provide me some appropriate link/reference which may solve my issue.
MainApplication.java
#SpringBootApplication
public class Application
{
public static void main(String[] args)
{
ApplicationContext context = SpringApplication.run(NetappApplication.class, args);
Sample sample = context.getBean(Sample.class);
System.out.println(sample.check(null));
}
}
Sample.java
public interface Sample
{
public String check(String message);
}
SampleImpl.java
#Service
#PropertySource("classpath:app.properties")
public class SampleImpl implements Sample
{
#Value("${test}")
String message1;
#Override
public String check(#Value("${test}") String message)
{
return message;
}
}
app.properties
test=anand
But you are passing null to your method...
Perhaps what you want to do is to assign default value to test in case it's not defined in property file:
#Value("${test:default}");
Then, when properties are autowired by Spring if placeholder resolver doesn't get the value from props file, it will use what is after :.
The best use case for this (that I can think of) is when you create Spring configuration.
Let's say you have a configuration class: for DB access. Simply put:
#Configuration
public class DbConfig {
#Value("${url:localhost}")
String dbUrl;
// rest for driver, user, pass etc
public DataSource createDatasource() {
// here you use some DataSourceBuilder to configure connection
}
}
Now, when Spring application starts up, properties' values are resolved, and as I wrote above you can switch between value from property and a default value. But it is done once, when app starts and Spring creates your beans.
If you want to check incoming argument on runtime, simple null check will be enough.
#Value("${test}")
String message1;
#Override
public String check(String message) {
if (message == null) {
return message1;
}
}
I have a properties file containing values like jdbc.password={enc}laksksjdjdj
Using JDK 1.7 and Spring 4.1.5 my configuration class looks like this
#PropertySources({
#PropertySource("classpath:application.properties"),
#PropertySource("classpath:env.properties")
})
#ComponentScan("com.acme")
#Configuration
public class SpringConfig
{
#Autowired
private ConfigurableEnvironment env;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer()
{
return new EncryptedPropertySourcesPlaceholderConfigurer();
}
}
What I am trying to achieve is to translate any values in my properties file from an encrypted value to the actual value. Some values will be encrypted and some won't. This is what I have attempted so far and when I place a breakpoint on convertProperties() the props argument is always empty. I can't make any sense of this because I can see that at this point this.environment is loaded with all the properties from the files.
public class EncryptedPropertySourcesPlaceholderConfigurer extends PropertySourcesPlaceholderConfigurer
{
#Override
protected void convertProperties(Properties props)
{
Enumeration<?> propertyNames = props.propertyNames();
while (propertyNames.hasMoreElements()) {
String propertyName = (String) propertyNames.nextElement();
String propertyValue = props.getProperty(propertyName);
// String convertedValue = <translate the value>;
props.setProperty(propertyName, convertedValue);
}
}
#Override
protected Properties mergeProperties() throws IOException {
final Properties mergedProperties = super.mergeProperties();
convertProperties(mergedProperties);
return mergedProperties;
}
}
Has anyone been able to achieve this using PropertySourcesPlaceholderConfigurer? I have similar logic working in older applications using PropertyPlaceholderConfigurer but wanted to use the newer Spring configuration.
I noticed that Jasypt has a similar EncryptablePropertySourcesPlaceholderConfigurer but this behaves in the same fashion, so I'm confused.
For some reason using the #PropertySources annotation(s) does not work as expected.
When EncryptedPropertySourcesPlaceholderConfigurer.convertProperties() is called the autowired ConfigurableEnvironment does not contain the entries from my properties files. There are no references to my listed properties files at all.
To get around this limitation I removed the annotations and explicitly loaded these resources in in the config class. So it now reads like this
#ComponentScan("com.acme")
#Configuration
public class SpringConfig
{
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer()
{
EncryptedPropertySourcesPlaceholderConfigurer p = new EncryptedPropertySourcesPlaceholderConfigurer(new KeyfileDecryptor());
p.setLocations(
new ClassPathResource("application.properties"),
new ClassPathResource("env.properties")
);
return p;
}
#Bean
public DataSource dataSource(
#Value("${jdbc.driverclassname}") String driverclassname,
#Value("${jdbc.url}") String url,
#Value("${jdbc.username}") String username,
#Value("${jdbc.password}") String password)
{
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverclassname);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
This change introduced the issue that the autowired ConfigurableEnvironment will be null so I had to change to using #Value injection in order to configure my dataSource from the properties.
Everything now works as expected and EncryptedPropertySourcesPlaceholderConfigurer.convertProperties() receives all of the properties values from both files.
Written one sample application to do add support for using Encrypted values here
https://github.com/pushpendra-jain/spring-examples/tree/master/PropertySourcesPlaceholderEncrypter
Basically instead of trying to invoke its convertProperty method overrided its processProperties method and getting the job done and code surely does not change any existing behavior.
see https://github.com/pushpendra-jain/spring-examples/blob/master/README.md for steps.