Spring cung cấp cho chúng ta nhiều cách để có thể đọc các tập tin cấu hình của ứng dụng là các properties files. Trong bài viết này, mình sẽ trình bày với các bạn cách phổ biến nhất đó chính là sử dụng đối tượng PropertyPlaceholderConfigurer, các bạn nhé!
Đầu tiên, mình sẽ tạo một Maven project để làm ví dụ:
Với class Application có nội dung như sau:
1 2 3 4 5 6 7 8 9 10 11 |
package com.huongdanjava.springpropertyplaceholderconfigurer; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Application { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml"); } } |
Mình sẽ sử dụng Java 17 cho ví dụ này:
1 2 3 4 |
<properties> <maven.compiler.source>17</maven.compiler.source> <maven.compiler.target>17</maven.compiler.target> </properties> |
Spring framework dependency:
1 2 3 4 5 |
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>6.0.3</version> </dependency> |
Trong project của mình, tập tin spring.xml chính là tập tin cấu hình của Spring còn tập tin configuration.properties là tập tin cấu hình của ứng dụng. Nội dung của những tập tin này như sau:
spring.xml
1 2 3 4 5 |
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans> |
configuration.properties
1 |
app.author=Khanh Nguyen |
Bây giờ mình sẽ sử dụng đối tượng PropertyPlaceholderConfigurer để đọc những properties có trong tập tin configuration.properties ở trên lên cho ứng dụng của chúng ta sử dụng nha các bạn!
Đơn giản nhất, chúng ta có thể khai báo đối tượng PropertyPlaceholderConfigurer trong tập tin cấu hình của Spring như sau:
1 2 3 |
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:configuration.properties"/> </bean> |
Với khai báo này, Spring sẽ sử dụng đường dẫn là giá trị của property location để tìm kiếm tập tin properties và đọc tất cả các properties có trong tập tin này lên. Trong khai báo trên của mình, mình đã sử dụng biến classpath, tương ứng với đường dẫn /src/main/resources, rồi cộng với tên tập tin.
Trong quá trình start ứng dụng, nếu Spring không thể tìm thấy tập tin properties mà chúng ta đã khai báo, nó sẽ báo lỗi ngay. Ví dụ, giờ mình rename tập tin configuration.properties thành configuration1.properties thì nó sẽ báo lỗi như sau:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/bin/java -Didea.launcher.port=7533 "-Didea.launcher.bin.path=/Applications/IntelliJ IDEA.app/Contents/bin" -Dfile.encoding=UTF-8 -classpath "/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/charsets.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/deploy.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/cldrdata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/dnsns.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/jaccess.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/jfxrt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/localedata.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/nashorn.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/sunec.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/sunjce_provider.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/sunpkcs11.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/ext/zipfs.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/javaws.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/jce.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/jfr.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/jfxswt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/jsse.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/management-agent.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/plugin.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/resources.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/jre/lib/rt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/ant-javafx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/dt.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/javafx-mx.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/jconsole.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/packager.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/sa-jdi.jar:/Library/Java/JavaVirtualMachines/jdk1.8.0_111.jdk/Contents/Home/lib/tools.jar:/Users/khanh/Working/Code/huongdanjava.com/spring-read-properties-files-using-propertyplaceholderconfigurer/target/classes:/Users/khanh/.m2/repository/org/springframework/spring-context/4.3.8.RELEASE/spring-context-4.3.8.RELEASE.jar:/Users/khanh/.m2/repository/org/springframework/spring-aop/4.3.8.RELEASE/spring-aop-4.3.8.RELEASE.jar:/Users/khanh/.m2/repository/org/springframework/spring-beans/4.3.8.RELEASE/spring-beans-4.3.8.RELEASE.jar:/Users/khanh/.m2/repository/org/springframework/spring-core/4.3.8.RELEASE/spring-core-4.3.8.RELEASE.jar:/Users/khanh/.m2/repository/commons-logging/commons-logging/1.2/commons-logging-1.2.jar:/Users/khanh/.m2/repository/org/springframework/spring-expression/4.3.8.RELEASE/spring-expression-4.3.8.RELEASE.jar:/Applications/IntelliJ IDEA.app/Contents/lib/idea_rt.jar" com.intellij.rt.execution.application.AppMain com.huongdanjava.springpropertyplaceholderconfigurer.Application Jun 05, 2017 5:57:33 PM org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh INFO: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3d646c37: startup date [Mon Jun 05 17:57:33 ICT 2017]; root of context hierarchy Jun 05, 2017 5:57:34 PM org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions INFO: Loading XML bean definitions from class path resource [spring.xml] Jun 05, 2017 5:57:35 PM org.springframework.context.support.ClassPathXmlApplicationContext refresh WARNING: Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanInitializationException: Could not load properties; nested exception is java.io.FileNotFoundException: class path resource [configuration.properties] cannot be opened because it does not exist Exception in thread "main" org.springframework.beans.factory.BeanInitializationException: Could not load properties; nested exception is java.io.FileNotFoundException: class path resource [configuration.properties] cannot be opened because it does not exist at org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:89) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:281) at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:161) at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:686) at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:524) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139) at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83) at com.huongdanjava.springpropertyplaceholderconfigurer.Application.main(Application.java:9) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at com.intellij.rt.execution.application.AppMain.main(AppMain.java:147) Caused by: java.io.FileNotFoundException: class path resource [configuration.properties] cannot be opened because it does not exist at org.springframework.core.io.ClassPathResource.getInputStream(ClassPathResource.java:172) at org.springframework.core.io.support.EncodedResource.getInputStream(EncodedResource.java:154) at org.springframework.core.io.support.PropertiesLoaderUtils.fillProperties(PropertiesLoaderUtils.java:98) at org.springframework.core.io.support.PropertiesLoaderSupport.loadProperties(PropertiesLoaderSupport.java:177) at org.springframework.core.io.support.PropertiesLoaderSupport.mergeProperties(PropertiesLoaderSupport.java:158) at org.springframework.beans.factory.config.PropertyResourceConfigurer.postProcessBeanFactory(PropertyResourceConfigurer.java:80) ... 12 more |
Để bỏ qua lỗi này thì các bạn hãy khai báo thêm một thuộc tính nữa ignoreResourceNotFound cho đối tượng PropertyPlaceholderConfigurer như sau:
1 2 3 4 |
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="location" value="classpath:configuration.properties"/> <property name="ignoreResourceNotFound" value="true"/> </bean> |
Trong trường hợp mà ứng dụng của các bạn có nhiều tập tin cấu hình thì để đọc hết tất cả các properties trong các tập tin cấu hình đó, các bạn phải sử dụng thuộc tính locations của đối tượng PropertyPlaceholderConfigurer. Ví dụ, mình tạo thêm một tập tin database.properties:
để chứa các thông tin liên quan đến kết nối tới database cho ứng dụng với nội dung như sau:
1 2 3 4 |
database.host=localhost database.port=3306 database.user=root database.pass=123456 |
Để đọc những thông tin database này nữa thì các bạn phải sửa lại khai báo cho đối tượng PropertyPlaceholderConfigurer như sau:
1 2 3 4 5 6 7 8 |
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:configuration.properties</value> <value>classpath:database.properties</value> </list> </property> </bean> |
Tinh Huynh
Làm sao lấy được giá trị của key “database.host” ?
Ví dụ: khi đọc file bằng class Properties ta có cách lấy giá trị như sau
Properties prop = new Properties();
prop.load(….);
System.out.println(prop.getProperty(“database.host”));
Còn class PropertyPlaceholderConfigurer thì lấy bằng cách nào?
Khanh Nguyen
Đọc thêm bài viết này nhé bạn https://huongdanjava.com/vi/cach-su-dung-properties-trong-tap-tin-cau-hinh-cua-spring.html
Trịnh Đức Minh
Làm sao để đọc được các field trong config file từ một PropertyPlaceholderConfigurer
Khanh Nguyen
Các field ở đây của em là gì thế?