Mocking a Properties file with Mockito in Spring - java

I am trying to write a unit test for the following method in my controller.
#Autowired
private ApplicationContext context;
private String getProperty() {
try {
Properties props = context.getBean("myProperties", Properties.class);
String val = props.getProperty("myProperty");
......
The Bean is declared like this in my applicationContext:
<util:properties id="myProperties" scope="prototype" location="file:${catalina.base}/webapps/myProperties.properties"/>
How can I mock this so that I can test different values of the val variable?
I thought about creating a test properties file and mocking it like this:
context = Mockito.mock(ApplicationContext.class);
Mocikto.when(context.getBean("myProperties", Properties.class)).thenReturn(some test file)
but then I would have to declare the test file as a bean somewhere.
I was wondering if there was an easier way to do this?
Thanks

If you're using spring-3, you can do:
<context:property-placeholder location="myprops.properties" />
And in your code:
#Value("${myProperty}")
private String myProp;
public String getMyProp() {
return myProp;
}
This causes myprops.properties to be made available for variable substitutions via ${...} expressions, and the #Value annotation allows value injection of properties. Then in your unit test you can simply set different values of myProp.

The easier way is to use a org.springframework.beans.factory.config.PropertyPlaceholderConfigurer instead of pulling the properties explicitly from the spring application context. The PropertyPlaceholderConfigurer injects your bean with the properties you specify. Then you don't need Mockito at all, in the test you set the property value in the Controller to whatever you want it to be.
So you'd set up the configurer in the application context xml:
<context:property-placeholder
location="file:${catalina.base}/webapps/myProperties.properties"/>
and add some configuration for your controller (I expect there's a way to do this with annotations but don't know it):
<bean id="whateverMyControllerIdIs" class="com.initech.foobar.MyControllerImpl">
<property name="quux"><value>${myProperty}</value></property>
</bean>
where the controller has an instance variable that you want to populate with the property, with a setter, like this:
String quux;
public void setQuux(String quux) {this.quux = quux;}
Just saw a blog post on Spring 3.1 enhancements, here's the new xml-free way to do this:
#Configuration
#PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
#Autowired
Environment env;
#Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}

So I can test without having to load the Spring Context I use a Config class for accessing all of the properties file(s) values from within code. The benefits are:
1) Spring doesn't load in your unit tests
2) You can force an Exception if the property is missing and it is required
3) You can return strongly type property values from the getter() methods (i.e. convert to a Date)
4) The "key" values expected from your properties files are documented in a single Java class (i.e. public static final PROP_XXX)
#Component
public class Config {
public static final String PROP_USER_NAME = "user.name";
private Properties applicationProperties;
/*** Functional methods ***/
/**
* Helper method to ensure consistent Exception handling where requested property values are missing
* from the properties files and they are "required" for the application to function correctly.
*
* #param key
* #return The String value of the property requested
*/
private String readPropertyRequired(String key) {
String value = readProperty(key);
if(StringUtils.isBlank(value)) {
throw new PropertyNotFoundException(key);
}
return value;
}
/**
* Helper method to return String values from the properties files that have been loaded
*
* #param key
* #return The String value of the property requested or NULL if empty
*/
private String readProperty(String key) {
return applicationProperties.getProperty(key);
}
/*** Getters & Setters ***/
#Autowired
public void setApplicationProperties(Properties applicationProperties) {
this.applicationProperties = applicationProperties;
}
public String getUserName() {
return readPropertyRequired(PROP_USER_NAME);
}
}
You can then unit test this class by simply injecting a standard java.util.Properties

Related

How can I interpolate a class constant in a Spring `#Value` annotation?

I want to let Spring assign a property value.
public class Foobar {
#Value("${example.property.foo:bar}")
private String foo;
}
Let's say I want to refer to example.property.foo in several different places, so I'd rather assign the flag as a constant on Foobar:
public class Foobar {
public static final String FOO_PROPERTY_FLAG = "example.property.foo";
}
The setting of example.property.foo=whatever happens elsewhere (as a system property, or in a #TestPropertySource).
How can I refer to FOO_PROPERTY_FLAG in the annotation? This works:
#Value("${" + FOO_PROPERTY_FLAG + ":bar}")
But it's kind of ugly. Can I use the "#{}" expression syntax here somehow?
#Value("${#{FOO_PROPERTY_FLAG}:bar}") // doesn't work; value is never injected
You can do something like:
public static final String KEY = "propertyName";
#Value("#{T(a.b.c.package.MyConstants).KEY}")
The important part is to specify package and class. Otherwise spring will try to lookup constant in BeanExpressionContext which is actually executing your SpEL
private #Value("${propertyName}") String propertyField;
No getters or setters!
With the properties being loaded via the config:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="classpath:propertyFile.properties" name="propertiesBean"/>
There's also the totally non-Xml version:
#PropertySource("classpath:propertyFile.properties")
public class AppConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
Make sure and add in the namespace URI xmlns:p="springframework.org/schema/p"; to use the p: prefixed attributes

Inject value in a spring bean from constants java file

I have an application in which in a spring bean, value is being injected from a property file.
I want to inject value from a constants.java file.
What changes are do I need to make.
Spring bean
#Value("${resetPassword.email.key}")
private String resetPassword;
Property file
resetPassword.email.key = RESET_PASSWORD
Constants.java
public static final String resetPassword_email_key = "RESET_PASSWORD";
You can't reference java constant in properties file. And you don't need Spring injection for that. You simply do
private String resetPassword = Constants.resetPassword_email_key;
If you need to share Constants class between multiple sub-projects (modules of a project), you may want to extract this class to a library that may be included into other projects.
You cannot inject a value to static properties. But you can assign to non-static setter as below:
#Component
public class GlobalValue {
public static String DATABASE;
#Value("${mongodb.db}")
public void setDatabase(String db) {
DATABASE = db;
}
}
From https://www.mkyong.com/spring/spring-inject-a-value-into-static-variables/
You can inject value with #Value from properties file or from spring bean.
To use properties file declare it with util:properties tag:
<util:properties id="jdbcProperties" location="classpath:org/example/config/jdbc.properties"/>
And in spring bean do :
private #Value("#{jdbcProperties.url}") String jdbcUrl;
private #Value("#{jdbcProperties.username}") String username;
private #Value("#{jdbcProperties.password}") String password;
Or you can inject value of another spring bean like:
#Value("#{strategyBean.databaseKeyGenerator}")
public void setKeyGenerator(KeyGenerator kg) { … }
More info at http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/new-in-3.html

How to access a value defined in the application.properties file in Spring Boot

I want to access values provided in application.properties, e.g.:
logging.level.org.springframework.web: DEBUG
logging.level.org.hibernate: ERROR
logging.file=${HOME}/application.log
userBucket.path=${HOME}/bucket
I want to access userBucket.path in my main program in a Spring Boot application.
You can use the #Value annotation and access the property in whichever Spring bean you're using
#Value("${userBucket.path}")
private String userBucketPath;
The Externalized Configuration section of the Spring Boot docs, explains all the details that you might need.
Another way is injecting org.springframework.core.env.Environment to your bean.
#Autowired
private Environment env;
....
public void method() {
.....
String path = env.getProperty("userBucket.path");
.....
}
#ConfigurationProperties can be used to map values from .properties( .yml also supported) to a POJO.
Consider the following Example file.
.properties
cust.data.employee.name=Sachin
cust.data.employee.dept=Cricket
Employee.java
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
#ConfigurationProperties(prefix = "cust.data.employee")
#Configuration("employeeProperties")
public class Employee {
private String name;
private String dept;
//Getters and Setters go here
}
Now the properties value can be accessed by autowiring employeeProperties as follows.
#Autowired
private Employee employeeProperties;
public void method() {
String employeeName = employeeProperties.getName();
String employeeDept = employeeProperties.getDept();
}
Currently, I know about the following three ways:
1. The #Value annotation
#Value("${<property.name>}")
private static final <datatype> PROPERTY_NAME;
In my experience there are some situations when you are not
able to get the value or it is set to null.
For instance, when you try to set it in a preConstruct() method or an init() method. This happens because the value injection happens after the class is fully constructed. This is why it is better to use the third option.
2. The #PropertySource annotation
#PropertySource("classpath:application.properties")
// 'env' is an Environment variable
env.getProperty(configKey);
PropertySouce sets values from the property source file in an Environment variable (in your class) when the class is loaded.
So you able to fetch easily afterword.
Accessible through System Environment variable.
3. The #ConfigurationProperties annotation.
This is mostly used in Spring projects to load configuration properties.
It initializes an entity based on property data.
#ConfigurationProperties identifies the property file to load.
#Configuration creates a bean based on configuration file variables.
#ConfigurationProperties(prefix = "user")
#Configuration("UserData")
class user {
// Property & their getter / setter
}
#Autowired
private UserData userData;
userData.getPropertyName();
You can do it this way as well....
#Component
#PropertySource("classpath:application.properties")
public class ConfigProperties {
#Autowired
private Environment env;
public String getConfigValue(String configKey){
return env.getProperty(configKey);
}
}
Then wherever you want to read from application.properties, just pass the key to getConfigValue method.
#Autowired
ConfigProperties configProp;
// Read server.port from app.prop
String portNumber = configProp.getConfigValue("server.port");
Follow these steps.
Create your configuration class like below. You can see:
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.beans.factory.annotation.Value;
#Configuration
public class YourConfiguration {
// Passing the key which you set in application.properties
#Value("${userBucket.path}")
private String userBucket;
// Getting the value from that key which
// you set in application.properties
#Bean
public String getUserBucketPath() {
return userBucket;
}
}
When you have a configuration class then inject in the variable from a configuration where you need.
#Component
public class YourService {
#Autowired
private String getUserBucketPath;
// Now you have a value in the getUserBucketPath
// variable automatically.
}
You can use the #Value to load variables from the application.properties if you will use this value in one place, but if you need a more centralized way to load these variables #ConfigurationProperties is a better approach.
Additionally, you can load variables and cast them automatically if you need different data types to perform your validations and business logic.
application.properties
custom-app.enable-mocks = false
#Value("${custom-app.enable-mocks}")
private boolean enableMocks;
You can use #Value("${property-name}") from the
application.properties if your class is annotated with
#Configuration or #Component.
There's one more way I tried out was making a Utility class to read properties in the following way -
protected PropertiesUtility () throws IOException {
properties = new Properties();
InputStream inputStream =
getClass().getClassLoader().getResourceAsStream("application.properties");
properties.load(inputStream);
}
You can make use of static method to get the value of the key passed as the parameter.
You should inject #Autowired private Environment env; from import org.springframework.core.env.Environment;
And then use it this way:
env.getProperty("yourPropertyNameInApplication.properties")
#Value Spring annotation is used for injecting values into fields in Spring-manged beans, and it can be applied to the field or constructor/method parameter level.
Examples
String value from the annotation to the field
#Value("string value identifire in property file")
private String stringValue;
We can also use the #Value annotation to inject a Map property.
First, we'll need to define the property in the {key: ‘value' } form in our properties file:
valuesMap={key1: '1', key2: '2', key3: '3'}
Not that the values in the Map must be in single quotes.
Now inject this value from the property file as a Map:
#Value("#{${valuesMap}}")
private Map<String, Integer> valuesMap;
To get the value of a specific key
#Value("#{${valuesMap}.key1}")
private Integer valuesMapKey1;
We can also use the #Value annotation to inject a List property.
#Value("#{'${listOfValues}'.split(',')}")
private List<String> valuesList;
To pick the values from property file, we can have a Config reader class, something like ApplicationConfigReader.java.
Then define all the variables against properties. Refer to the below example,
application.properties
myapp.nationality: INDIAN
myapp.gender: Male
Below is the corresponding reader class.
#Component
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "myapp")
class AppConfigReader{
private String nationality;
private String gender
// Getter and setter
}
Now we can auto-wire the reader class wherever we want to access property values.
E.g.,
#Service
class ServiceImpl{
#Autowired
private AppConfigReader appConfigReader;
//...
// Fetching values from the configuration reader
String nationality = appConfigReader.getNationality() ;
String gender = appConfigReader.getGender();
}
An application can read three types of values from the application.properties file.
application.properties
my.name = kelly
my.dbConnection = {connection_srting:'http://localhost:...', username:'benz', password:'pwd'}
Class file
#Value("${my.name}")
private String name;
#Value("#{${my.dbConnection}}")
private Map<String,String> dbValues;
If you don't have a property in application.properties then you can use the default value:
#Value("${your_name: default value}")
private String msg;
You can use the #Value annotation for reading values from an application.properties/yml file.
#Value("${application.name}")
private String applicationName;
There are 3 ways to read the application.properties,
using #Value, EnvironmentInterface and #ConfigurationProperties..
#Value(${userBucket.path})
private String value;
2nd way:
#Autowired
private Environment environment;
String s = environment.getProperty("userBucket.path");
3rd way:
#ConfigurationProperties("userbucket")
public class config {
private String path;
//getters setters
}
Can be read with getters and setters..
Reference - here
Injecting a property with the #Value annotation is straightforward:
#Value("${jdbc.url}")
private String jdbcUrl;
We can obtain the value of a property using the Environment API
#Autowired
private Environment env;
...
dataSource.setUrl(env.getProperty("jdbc.url"));
Another way to find a key/value in the configuration.
...
import org.springframework.core.env.ConfigurableEnvironment;
...
#SpringBootApplication
public class MyApplication {
#Autowired
private ConfigurableEnvironment myEnv;
...
#EventListener(ApplicationReadyEvent.class)
public void doSomethingAfterStartup()
throws Exception {
LOG.info("myEnv (userBucket.path): " + myEnv.getProperty("userBucket.path"));
}
}
You can access the application.properties file values by using:
#Value("${key_of_declared_value}")
The best ways to get property values are using:
1. Using Value annotation
#Value("${property.key}")
private String propertyKeyVariable;
2. Using the Environment bean
#Autowired
private Environment env;
public String getValue() {
return env.getProperty("property.key");
}
public void display() {
System.out.println("# Value : " + getValue);
}
Spring Boot allows us several methods to provide externalized configurations. You can try using file application.yml or YAML files instead of the property file and provide different property files setup according to different environments.
We can separate out the properties for each environment into separate YAML files under separate Spring profiles. Then during deployment you can use:
java -jar -Drun.profiles=SpringProfileName
to specify which Spring profile to use. Note that the YAML files should be named like
application-{environmentName}.yml
for them to be automatically taken up by Spring Boot.
Reference: 2. Externalized Configuration
To read from the application.yml or property file:
The easiest way to read a value from the property file or YAML is to use the Spring #value annotation. Spring automatically loads all values from the YAML file to the Spring environment, so we can directly use those values from the environment like:
#Component
public class MySampleBean {
#Value("${name}")
private String sampleName;
// ...
}
Or another method that Spring provides to read strongly-typed beans is as follows:
YML
ymca:
remote-address: 192.168.1.1
security:
username: admin
Corresponding POJO to read the YAML content:
#ConfigurationProperties("ymca")
public class YmcaProperties {
private InetAddress remoteAddress;
private final Security security = new Security();
public boolean isEnabled() { ... }
public void setEnabled(boolean enabled) { ... }
public InetAddress getRemoteAddress() { ... }
public void setRemoteAddress(InetAddress remoteAddress) { ... }
public Security getSecurity() { ... }
public static class Security {
private String username;
private String password;
public String getUsername() { ... }
public void setUsername(String username) { ... }
public String getPassword() { ... }
public void setPassword(String password) { ... }
}
}
The above method works well with YAML files.
Reference: 2. Externalized Configuration
There are two ways to access the value from the application.properties file:
Using the #Value annotation
#Value("${property-name}")
private data_type var_name;
Using an instance of the Environment class
#Autowired
private Environment environment;
// Access this way in the method where it's required
data_type var_name = environment.getProperty("property-name");
You can also inject an instance of the environment using constructor injection or creating a bean yourself.
Try class PropertiesLoaderUtils. This approach doesn’t use any annotation of Spring Boot. It is a traditional class way.
Example:
Resource resource = new ClassPathResource("/application.properties");
Properties props = PropertiesLoaderUtils.loadProperties(resource);
String url_server=props.getProperty("server_url");
Use the getProperty() method to pass the key and access the value in the properties file.
There are actually three ways to read the application.properties file,
Using Environment,
#Autowired
Environment environment
environment.getProperty({propertyName})
Or we can use #Value,
#Value("${property}")
but the problem with #Value is it might throw an exception if the value is not in the properties file.
The suggested way is using #ConfigurationProperties
#ConfigurationProperties("userBucket")
public class test{
private String path;
//getters and setters
}
For a detailed example - Reading application.properties.
The best thing is to use the #Value annotation. It will automatically assign a value to your object private Environment en.
This will reduce your code, and it will be easy to filter your files.
There are two ways,
you can directly use #Value in your class
#Value("#{'${application yml field name}'}")
public String ymlField;
Or
To make it clean you can clean #Configuration class where you can add all your #value
#Configuration
public class AppConfig {
#Value("#{'${application yml field name}'}")
public String ymlField;
}
application.yml or application.properties
config.value1: 10
config.value2: 20
config.str: This is a simle str
MyConfig class
#Configuration
#ConfigurationProperties(prefix = "config")
public class MyConfig {
int value1;
int value2;
String str;
public int getValue1() {
return value1;
}
// Add the rest of getters here...
// Values are already mapped in this class. You can access them via getters.
}
Any class that wants to access config values
#Import(MyConfig.class)
class MyClass {
private MyConfig myConfig;
#Autowired
public MyClass(MyConfig myConfig) {
this.myConfig = myConfig;
System.out.println( myConfig.getValue1() );
}
}
The easiest way would be to use the #Value annotation provided by Spring Boot. You need to define a variable at class level. For example:
#Value("${userBucket.path}")
private String userBucketPath
There is another way you can do this via the Environment Class. For example:
Autowire the environment variable to your class where you need to access this property:
#Autowired
private Environment environment
Use the environment variable to get the property value in the line you need it using:
environment.getProperty("userBucket.path");
Hope this answers your question!
To read application.properties or application.yml attributes follow the following steps:
Add your attributes in application.properties or application.yaml
Create config class and add your attributes
application.jwt.secretKey=value
application.jwt.tokenPrefix=value
application.jwt.tokenExpiresAfterDays=value ## 14
application:
jwt:
secret-key: value
token-prefix: value
token-expires-after-days: value ## 14
#Configuration("jwtProperties") // you can leave it empty
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "application.jwt") // prefix is required
public class JwtConfig {
private String secretKey;
private String tokenPrefix;
private int tokenExpiresAfterDays;
// getters and setters
}
NOTE: in .yaml file you have to use kabab-case
Now to use the config class just instantiate it, you can do this manualy or with dependency injection.
public class Target {
private final JwtConfig jwtConfig;
#Autowired
public Target(JwtConfig jwtConfig) {
this.jwtConfig = jwtConfig;
}
// jwtConfig.getSecretKey()
}
For me, none of the above did directly work for me.
I did the following:
In addition to Rodrigo Villalba Zayas' answer, I added implements InitializingBean to the class and implemented the method
#Override
public void afterPropertiesSet() {
String path = env.getProperty("userBucket.path");
}
So that will look like
import org.springframework.core.env.Environment;
public class xyz implements InitializingBean {
#Autowired
private Environment env;
private String path;
....
#Override
public void afterPropertiesSet() {
path = env.getProperty("userBucket.path");
}
public void method() {
System.out.println("Path: " + path);
}
}
I had this problem too. But there is a very simple solution. Just declare your variable in the constructor.
My example:
application.propperties:
#Session
session.timeout=15
SessionServiceImpl class:
private final int SESSION_TIMEOUT;
private final SessionRepository sessionRepository;
#Autowired
public SessionServiceImpl(#Value("${session.timeout}") int sessionTimeout,
SessionRepository sessionRepository) {
this.SESSION_TIMEOUT = sessionTimeout;
this.sessionRepository = sessionRepository;
}
You can use #ConfigurationProperties. It's simple and easy to access a value defined in application.properties:
# Datasource
app.datasource.first.jdbc-url=jdbc:mysql://x.x.x.x:3306/ovtools?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=UTC
app.datasource.first.username=
app.datasource.first.password=
app.datasource.first.driver-class-name=com.mysql.cj.jdbc.Driver
server.port=8686
spring.jpa.hibernate.ddl-auto=update
spring.jpa.generate-ddl=true
spring.jpa.show-sql=true
spring.jpa.database=mysql
#Slf4j
#Configuration
public class DataSourceConfig {
#Bean(name = "tracenvDb")
#Primary
#ConfigurationProperties(prefix = "app.datasource.first")
public DataSource mysqlDataSourceanomalie() {
return DataSourceBuilder.create().build();
}
#Bean(name = "JdbcTemplateenv")
public JdbcTemplate jdbcTemplateanomalie(#Qualifier("tracenvDb") DataSource datasourcetracenv) {
return new JdbcTemplate(datasourcetracenv);
}

Can I manually load #ConfigurationProperties without the Spring AppContext?

Is there any way to load a class marked with #ConfigurationProperties without using a Spring Context directly? Basically I want to reuse all the smart logic that Spring does but for a bean I manually instantiate outside of the Spring lifecycle.
I have a bean that loads happily in Spring (Boot) and I can inject this into my other Service beans:
#ConfigurationProperties(prefix="my")
public class MySettings {
String property1;
File property2;
}
See the spring docco for more info http://docs.spring.io/spring-boot/docs/current-SNAPSHOT/reference/htmlsingle/#boot-features-external-config-command-line-args
But now I need to access this bean from a class that is created outside of Spring (by Hibernate). The class is created so early in the app startup process that Spring Boot has not yet made the application context available through the classic lookup helper methods or roll-my-own static references.
So I instead want to do something like:
MySettings mySettings = new MySettings();
SpringPropertyLoadingMagicClass loader = new SpringPropertyLoadingMagicClass();
loader.populatePropertyValues(mySettings);
And have MySettings end up with all its values loaded, from the command line, system properties, app.properties, etc. Is there some class in Spring that does something like this or is it all too interwoven with the application context?
Obviously I could just load the Properties file myself, but I really want to keep Spring Boot's logic around using command line variables (e.g. --my.property1=xxx), or system variables, or application.properties or even a yaml file, as well as its logic around relaxed binding and type conversion (e.g. property2 is a File) so that it all works exactly the same as when used in the Spring context.
Possible or pipe dream?
Thanks for your help!
I had the same "issue".
Here is how I solved it in SpringBoot version 1.3.xxx and 1.4.1.
Let's say we have the following yaml configuration file:
foo:
apis:
-
name: Happy Api
path: /happyApi.json?v=bar
-
name: Grumpy Api
path: /grumpyApi.json?v=grrr
and we have the following ConfigurationProperties:
#ConfigurationProperties(prefix = "foo")
public class ApisProperties {
private List<ApiPath> apis = Lists.newArrayList();
public ApisProperties() {
}
public List<ApiPath> getApis() {
return apis;
}
public static class ApiPath {
private String name;
private String path;
public String getName() {
return name;
}
public void setName(final String aName) {
name = aName;
}
public String getPath() {
return path;
}
public void setPath(final String aPath) {
path = aPath;
}
}
}
Then, to do the "magic" things of Spring Boot programmatically (e.g. loading some properties in a static method), you can do:
private static ApisProperties apiProperties() {
try {
ClassPathResource resource;
resource = new ClassPathResource("/config/application.yml");
YamlPropertiesFactoryBean factoryBean;
factoryBean = new YamlPropertiesFactoryBean();
factoryBean.setSingleton(true); // optional depends on your use-case
factoryBean.setResources(resource);
Properties properties;
properties = factoryBean.getObject();
MutablePropertySources propertySources;
propertySources = new MutablePropertySources();
propertySources.addLast(new PropertiesPropertySource("apis", properties));
ApisProperties apisProperties;
apisProperties = new ApisProperties();
PropertiesConfigurationFactory<ApisProperties> configurationFactory;
configurationFactory = new PropertiesConfigurationFactory<>(apisProperties);
configurationFactory.setPropertySources(propertySources);
configurationFactory.setTargetName("foo"); // it's the same prefix as the one defined in the #ConfigurationProperties
configurationFactory.bindPropertiesToTarget();
return apisProperties; // apiProperties are fed with the values defined in the application.yaml
} catch (BindException e) {
throw new IllegalArgumentException(e);
}
}
Here's an update to ctranxuan's answer for Spring Boot 2.x. In our situation, we avoid spinning up a Spring context for unit tests, but do like to test our configuration classes (which is called AppConfig in this example, and its settings are prefixed by app):
public class AppConfigTest {
private static AppConfig config;
#BeforeClass
public static void init() {
YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
factoryBean.setResources(new ClassPathResource("application.yaml"));
Properties properties = factoryBean.getObject();
ConfigurationPropertySource propertySource = new MapConfigurationPropertySource(properties);
Binder binder = new Binder(propertySource);
config = binder.bind("app", AppConfig.class).get(); // same prefix as #ConfigurationProperties
}
}
The "magic" class you are looking for is PropertiesConfigurationFactory. But I would question your need for it - if you only need to bind once, then Spring should be able to do it for you, and if you have lifecycle issues it would be better to address those (in case they break something else).
This post is going into similar direction but extends the last answer with also validation and property placeholder resolutions.
Spring Boot Binder API support for #Value Annotations
#Value annotations in ConfigurationPropertys don't seem to bind properly though (at least if the referenced values are not part of the ConfigurationProperty's prefix namespace).
import org.springframework.boot.context.properties.bind.Binder
val binder = Binder.get(environment)
binder.bind(prefix, MySettings.class).get

Dynamically accessing properties using Spring 3.1's property abstraction

I'm trying to dynamically access properties from Spring's Environment property abstraction.
I declare my property files like this:
<context:property-placeholder
location="classpath:server.common.properties,
classpath:server.${my-environment}.properties" />
In my property file server.test.properties, I define the following:
myKey=foo
Then, given the following code:
#Component
public class PropertyTest {
#Value("${myKey}")
private String propertyValue;
#Autowired
private PropertyResolver propertyResolver;
public function test() {
String fromResolver = propertyResolver.getProperty("myKey");
}
}
When I run this code, I end up with propertyValue='foo', but fromResolver=null;
Receiving propertyValue indicates that the properties are being read, (and I know this from other parts of my code). However, attempting to look them up dynamically is failing.
Why? How can I dynamically look up property values, without having to use #Value?
Simply adding a <context:property-placeholder/> doesn't add a new PropertySource to the Environment. If you read the article you linked completely, you'll see it suggests registering an ApplicationContextInitializer in order to add new PropertySources so they'll be available in the way you're trying to use them.
To get this to work I had to split out the reading of the properties into a #Configuration bean, as shown here.
Here's the complete example:
#Configuration
#PropertySource("classpath:/server.${env}.properties")
public class AngularEnvironmentModuleConfiguration {
private static final String PROPERTY_LIST_NAME = "angular.environment.properties";
#Autowired
private Environment environment;
#Bean(name="angularEnvironmentProperties")
public Map<String,String> getAngularEnvironmentProperties()
{
String propertiesToInclude = environment.getProperty(PROPERTY_LIST_NAME, "");
String[] propertyNames = StringUtils.split(propertiesToInclude, ",");
Map<String,String> properties = Maps.newHashMap();
for (String propertyName : propertyNames)
{
String propertyValue = environment.getProperty(propertyName);
properties.put(propertyName, propertyValue);
}
return properties;
}
}
The set of properties are then injected elsewhere, to be consumed.

Categories

Resources