در این بخش به بحث چگونگی استفاده از Property ها در Spring و نحوه کانفیگ کردن آنها با PropertySource@ و روش XML ای آن و نحوه کارکرد آن در Spring Boot میپردازیم.
نحوه ثبت یک Property فایل توسط Annotation : در یک کلاس Configuration@ میتوان از PropertySource@ استفاده کنیم و آدرس فایل property را به عنوان آرگومان ارسال کنیم :
@Configuration @PropertySource("classpath:foo.properties") public class PropertiesWithJavaConfig { //... }
میتوانیم با استفاده از placeholder بصورت داینامیک آدرس property file را بدهیم اینطوری نیازی نیست که آدرس property فایل ثابت باشد :
@PropertySource({ "classpath:persistence-${envTarget:mysql}.properties" }) ...
برای معرفی چندین property فایل به این صورت میتوان اقدام کرد :
//java 8 feature @PropertySource("classpath:foo.properties") @PropertySource("classpath:bar.properties") public class PropertiesWithJavaConfig { //... }
@PropertySources({ @PropertySource("classpath:foo.properties"), @PropertySource("classpath:bar.properties") }) public class PropertiesWithJavaConfig { //... }
در این حالت اگر یک مقداری در فایل های property دوبار استفاده شود آخرین آنها در نظر گرفته میشود و الویت بالاتری دارد
طریقه تعریف توسط xml :
<context:property-placeholder location="classpath:foo.properties" />
در این حالت فایل foo.properties باید در مسیر /src/main/resources موجود باشد
حالت تعریف چند تایی در xml :
<context:property-placeholder location="classpath:foo.properties, classpath:bar.properties"/>
خواندن و استفاده کردن از مقادیر Property ها :
روش اول با استفاده از Value@ میتوانیم مقادیر موجود در property ها را خوانده و در متغییری تزریق کنیم
@Value( "${jdbc.url}" ) private String jdbcUrl;
خواندن مقادیر و در صورت عدم وجود آن، با یک مقدار پیش فرض پر میشود :
@Value( "${jdbc.url:aDefaultUrl}" ) private String jdbcUrl;
خواندن مقادیر در xml فایل ها :
<bean id="dataSource"> <property name="url" value="${jdbc.url}" /> </bean>
روش دوم با استفاده از Environment API در Spring :
@Autowired private Environment env; ... dataSource.setUrl(env.getProperty("jdbc.url"));
استفاده از property در Spring Boot :
Spring Boot در ابتدای اجرا دنبال یک فایل بصورت پیش فرض بنام application.properties در مسیر src/main/resources میگردد و بصورت اتوماتیک آنرا شناسایی و استفاده میکند و ما نیز میتوانیم تنظیمات خود را در داخل این فایل قرار دهیم و دیگر نیازی نداریم که فایل property خودمان را به Spring معرفی کنیم
همینطور میتوانیم فایل property تنظیمات را در خط فرمان موقع اجرا کد به عنوان آرگومان ارسال کنیم :
java -jar app.jar --spring.config.location=classpath:/another-location.properties
همچنین ما میتوانیم برای هر profile موجود یک فایل property مجزا در مسیر src/main/resources داشته باشیم که نیاز دارد الگوی زیر را داشته باشد. در صورت وجود این تنظیمات جانبی همچنان فایل application.properties به عنوان پیش فرض خوانده میشود
application-[environment].properties
مثل : application-dev.properties
اگر نیاز باشد کد با شرایط تستی تست شود و تنظیمات مختص خود را داشته باشد نیاز است که تنظیمات مربوطه در مسیر src/test/resources گذاشته شود
TestPropertySource@ : اگر در شرایط تست نیاز داریم که ادرس فایل تنظیمات property را بصورت دستی ارسال کنیم میتوانیم با این annotation این کار را بکنیم :
@ContextConfiguration @TestPropertySource("/my-test.properties") public class IntegrationTests { // tests }
حتی میتوانیم بدون ادرس دهی فایل، مقادیر و نام های property ها را بصورت دستی و hardcode ارسال کنیم :
@ContextConfiguration @TestPropertySource("foo=bar", "bar=foo") public class IntegrationTests { // tests }
همچنین میتوانیم مقادیر بالا را با کمک SpringBootTest@ وارد کنیم :
@SpringBootTest(properties = {"foo=bar", "bar=foo"}) public class IntegrationTests { // tests }
ConfigurationProperties@ : زمانی که property فایل های سلسله مراتبی داشته باشیم میتوانیم از این annotation استفاده کنیم
استفاده از yaml به جای properties فایل ها :
در Spring میتوانیم از فایل و ساختار yaml هم استفاده کنیم و تمامی شرایط آن با properties مشابه است و تنها فرق آن در ساختار آن و ساپورت نکردن PeropertySource@ از آن است حالا نگاهی کنیم به ساختار داخلی yaml :
مدل properties :
database.url=jdbc:postgresql:/localhost:5432/instance database.username=foo database.password=bar secret=foo
مدل yaml :
database: url: jdbc:postgresql:/localhost:5432/instance username: foo password: bar secret: foo
ارسال مقادیر property از طریق خط فرمان :
java -jar app.jar --property="value"
همچنین میتوانید از طریق سویچ سیستمی آنرا انجام دهیم :
java -Dproperty.name="value" -jar app.jar
از طریق متغییر های سیستمی هم میتوان نام و مقادیری را اعمال کنیم :
export name=value java -jar app.jar
تولید مقادیر تصادفی :
random.number=${random.int} random.long=${random.long} random.uuid=${random.uuid}
تعریف یک property خام با استفاده از کلاس PropertyPlaceholderConfigurer بصورت دستی:
این روش کاربرد زیادی ندارد ولی شاید شرایطی بوجود آید که نیاز باشد باید یک bean ایجاد کنیم که مقدار برگشتی آن از جنس کلاس PropertySourcesPlaceholderConfigurer باشد و اینطوری مقادیر property بصورت اتوماتیک ثبت خواهند شد :
@Bean public static PropertySourcesPlaceholderConfigurer properties(){ PropertySourcesPlaceholderConfigurer pspc = new PropertySourcesPlaceholderConfigurer(); Resource[] resources = new ClassPathResource[ ] { new ClassPathResource( "foo.properties" ) }; pspc.setLocations( resources ); pspc.setIgnoreUnresolvablePlaceholders( true ); return pspc; }
روش تعریف یک property خام بصورت دستی با xml :
این روش هم کاربرد زیادی ندارد
<bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"> <property name="locations"> <list> <value>classpath:foo.properties</value> </list> </property> <property name="ignoreUnresolvablePlaceholders" value="true"/> </bean>
Context to Context - Parent Child :
حالتی پیش میاد که (بیشتر در وب) که برنامه اینقدر بزرگ است که یک Context بزرگ چند Context داخلی در خودش دارد و اینجا مقادیر property چطور در Context پدر و فرزند قابل دسترس است ؟
- حالتی که property ها در فرمت xml و با استفاده از <property-placeholder> تعریف شده باشند :
اگر فایل در پدر تعریف شده باشد دسترسی ها به این نحو است :
@Value works in Child context: NO @Value works in Parent context: YES
اگر فایل در فرزند تعریف شده باشد دسترسی ها به این نحو است :
@Value works in Child context: YES @Value works in Parent context: NO
در مورد Environment API همانطور که قبلا توضیح دادیم چون با استفاده از xml و <property-placeholder> تعریف شده است دسترسی بکل وجود نخواهد داشت:
environment.getProperty works in either context: NO
- حالتی که property ها از طریق PropertySource@ تعریف شده باشند :
اگر فایل در پدر تعریف شده باشد دسترسی ها به این نحو است :
@Value works in Child context: YES @Value works in Parent context: YES environment.getProperty in Child context: YES environment.getProperty in Parent context: YES
اگر فایل در فرزند تعریف شده باشد دسترسی ها به این نحو است :
@Value works in Child context: YES @Value works in Parent context: NO environment.getProperty in Child context: YES environment.getProperty in Parent context: NO