In this post we’ll see how to externalize your configuration to a properties file in Spring and how to read values from
properties file in Spring using XML configuration and using @PropertySource
annotation.
It is a best practice to put application specific settings into separate properties files rather than hardcoding them with in the configuration. For example Database related configuration properties like DB Url, Driver class, user, password can be stored in a properties file and read from there or in an application for sending emails SMTP settings like host, user, password can be stored in a properties file.
- Properties file in Spring using XML configuration
- Specifying multiple properties file with XML
- Properties file in Spring using @PropertySource annotation
- @PropertySource with @Value to inject values
- @PropertySource with Environment to read property values
- Spring @PropertySource Features
- Ignore exception if value not found
- Property overriding with @PropertySource
Properties file in Spring using XML configuration
You can configure property placeholders using <context:property-placeholder>
in XML. The values to
replace are specified as placeholders of the form ${property-name}. At runtime, a
PropertySourcesPlaceholderConfigurer is applied to the metadata, it checks for placeholders in properties file and replace
placeholders values that match keys in the properties file.
Note that org.springframework.context.support.PropertySourcesPlaceholderConfigurer
is used from Spring
framework 5.2 version before that org.springframework.beans.factory.config.PropertyPlaceholderConfigurer class was used
which is deprecated from Spring 5.2.
When using <context:property-placeholder> element, a PropertySourcesPlaceholderConfigurer is registered automatically.
For example there is a app.properties file saved at location /src/main/resources/ so that it is on the classpath.
db.driverClassName=com.mysql.cj.jdbc.Driver db.url=jdbc:mysql://localhost:3306/knpcode db.username=user db.password=passwordXML Configuration
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!-- For properties files --> <context:property-placeholder location="classpath:app.properties" /> <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource"> <property name="driverClassName" value = "${db.driverClassName}" /> <property name="url" value = "${db.url}" /> <property name="username" value = "${db.username}" /> <property name="password" value = "${db.password}" /> </bean> </beans>
As you can see a properties file is registered using <context:property-placeholder> element by specifying the location of the properties file.
Specifying multiple properties file with XML
You can pass multiple properties files in location attribute as comma separated values.
<context:property-placeholder location="classpath:db.properties, classpath:mail.properties" />
Properties file in Spring using @PropertySource annotation
There is also a @PropertySource annotation which provides a convenient and declarative mechanism for adding a PropertySource to Spring’s Environment.
You can use @PropertySource annotation along with @Value annotation to inject values read from properties file but the better way is to use Spring’s Environment.
@PropertySource with @Value to inject values
@Configuration @ComponentScan(basePackages = "com.knpcode.springproject") @PropertySource("classpath:app.properties") public class AppConfig { @Value("${db.driverClassName}") private String driverClassName; @Value("${db.url}") private String url; @Value("${db.username}") private String userName; @Value("${db.password}") private String pwd; @Bean public DataSource dataSource() { DriverManagerDataSource ds = new DriverManagerDataSource(); ds.setDriverClassName(driverClassName); ds.setUrl(url); ds.setUsername(userName); ds.setPassword(pwd); return ds; } }
Note that PropertySourcesPlaceholderConfigurer class is used to resolve ${...} placeholders in @Value annotations against the current Spring Environment.
Running the example using the following class-
public class App { public static void main( String[] args ){ //EntityManager AbstractApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); DriverManagerDataSource dataSource = context.getBean("dataSource", DriverManagerDataSource.class); System.out.println("Driver class name- " + dataSource.getUsername()); context.close(); } }Output
Driver class name- user
@PropertySource with Environment to read property values
@PropertySource
annotation adds a PropertySource to Spring’s Environment so using using
getProperty()
method of Environment is a much convenient way to read properties rather than using @Value
annotation.
@Configuration @ComponentScan(basePackages = "com.knpcode.springproject") @PropertySource("classpath:app.properties") public class AppConfig { @Autowired private Environment env; @Bean public DataSource dataSource() { DriverManagerDataSource ds = new DriverManagerDataSource(); ds.setDriverClassName(env.getProperty("db.driverClassName")); ds.setUrl(env.getProperty("db.url")); ds.setUsername(env.getProperty("db.username")); ds.setPassword(env.getProperty("db.password")); return ds; } }
Notice how org.springframework.core.env.Environment is injected and that object is used to get properties.
Spring @PropertySource Features
- Any ${…} placeholders present in a @PropertySource resource location are resolved against the set of property sources already registered against the environment. For example
@Configuration @PropertySource("classpath:${app.path:default/path}/app.properties") public class AppConfig { .... .... }
If app.path is present in one of the property sources already registered (for example, system properties or environment variables), the placeholder is resolved to the corresponding value. If not, then default/path is used as a default. If no default is specified and a property cannot be resolved, an IllegalArgumentException is thrown.
Setting as system property- System.setProperty("app.path", "config"); - The @PropertySource annotation is repeatable, according to Java 8 conventions. All such @PropertySource annotations
need to be declared at the same level.
@Configuration @ComponentScan(basePackages = "com.knpcode.springproject") @PropertySource("classpath:app.properties") @PropertySource("classpath:global.properties") public class AppConfig { .. .. }
You can also use @PropertySources container annotation that aggregates several PropertySource annotations.
@Configuration @PropertySources({ @PropertySource("classpath:properties/db.properties"), @PropertySource("classpath:properties/mail.properties") }) public class Configurations { ... ... }
Ignore exception if value not found
If properties file is not found or passed key doesn’t exist in properties file Spring framework throws an exception by default. If you have a properties file that mayy or may not exist and you don’t want exception to be thrown if it doesn’t exist then you can specify ignoreResourceNotFound attribute as true.
With @PropertySource annotation@Configuration @PropertySource(value="classpath:properties/db.properties", ignoreResourceNotFound=true) public class DBConfiguration { }In XML Configuration
<context:property-placeholder location="classpath:config/db.properties" ignore-resource-not-found="false" />
With XML configuration you can also specify ignore-unresolvable
attribute to ignore an exception if
placeholder can't be resolved.
<context:property-placeholder location="classpath:config/db.properties" ignore-resource-not-found="false" ignore-unresolvable="false" />
Property overriding with @PropertySource
If a given property key exists in more than one .properties file, the last @PropertySource annotation processed will override the values for such duplicate keys.
For example, given two properties files a.properties and b.properties, consider the following two configuration classes that reference them with @PropertySource annotations:
@Configuration @PropertySource("classpath:/com/myco/a.properties") public class ConfigA { } @Configuration @PropertySource("classpath:/com/myco/b.properties") public class ConfigB { }
The override ordering depends on the order in which these classes are registered with the application context.
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ConfigA.class); ctx.register(ConfigB.class); ctx.refresh();
In the scenario above, the properties in b.properties will override any duplicates that exist in a.properties, because ConfigB was registered last.
That's all for the topic Read Values From Properties File in Spring. If something is missing or you have something to share about the topic please write a comment.
You may also like
No comments:
Post a Comment