Reload File Properties di Spring

1. Ikhtisar

Dalam tutorial ini, kami akan menunjukkan cara memuat ulang properti di aplikasi Spring.

2. Membaca Properti di Spring

Kami memiliki opsi berbeda untuk mengakses properti di Spring:

  1. Lingkungan - Kita dapat memasukkan Lingkungan dan kemudian menggunakan Lingkungan # getProperty untuk membaca properti tertentu. Lingkungan berisi sumber properti yang berbeda seperti properti sistem, parameter -D , dan application.properties (.yml) . Selain itu, sumber properti tambahan dapat ditambahkan ke Lingkungan menggunakan @PropertySource .
  2. Properties - Kita dapat memuat file properti ke dalam instance Properties , kemudian menggunakannya dalam bean dengan memanggil properties.get ("property").
  3. @Value - Kita bisa memasukkan properti tertentu dalam kacang dengan anotasi @Value ($ {'property'}) .
  4. @ConfigurationProperties - kita dapat menggunakan @ConfigurationProperties untuk memuat properti hierarki dalam kacang.

3. Memuat Ulang Properti dari File Eksternal

Untuk mengubah properti dalam file selama runtime, kita harus menempatkan file itu di suatu tempat di luar jar. Kemudian, kami akan memberi tahu Spring di mana dengan baris perintahparameter –spring.config.location = file: // {path to file} . Atau, kita bisa meletakkannya di application.properties.

Di properti berbasis file, kita harus memilih cara untuk memuat ulang file. Misalnya, kami dapat mengembangkan titik akhir atau penjadwal untuk membaca file dan memperbarui properti.

Satu pustaka yang berguna untuk memuat ulang file adalah konfigurasi umum Apache . Kita dapat menggunakan PropertiesConfiguration dengan ReloadingStrategy yang berbeda .

Mari tambahkan commons-configuration ke pom.xml kita :

 commons-configuration commons-configuration 1.10 

Kemudian, kami menambahkan metode untuk membuat kacang PropertiesConfiguration , yang akan kami gunakan nanti:

@Bean @ConditionalOnProperty(name = "spring.config.location", matchIfMissing = false) public PropertiesConfiguration propertiesConfiguration( @Value("${spring.config.location}") String path) throws Exception { String filePath = new File(path.substring("file:".length())).getCanonicalPath(); PropertiesConfiguration configuration = new PropertiesConfiguration( new File(filePath)); configuration.setReloadingStrategy(new FileChangedReloadingStrategy()); return configuration; }

Dalam kode di atas, kami telah menetapkan FileChangedReloadingStrategy sebagai strategi reload dengan penundaan penyegaran default. Ini berarti PropertiesConfiguration memeriksa tanggal modifikasi file jika pemeriksaan terakhirnya sebelum 5000ms yang lalu .

Kita dapat menyesuaikan penundaan menggunakan FileChangedReloadingStrategy # setRefreshDelay.

3.1. Memuat Ulang Properti Lingkungan

Jika kita ingin memuat ulang properti yang dimuat melalui instance Environment , kita harus memperluas PropertySource dan kemudian menggunakan PropertiesConfiguration untuk mengembalikan nilai baru dari file properti eksternal .

Mari kita mulai dengan memperluas PropertySource :

public class ReloadablePropertySource extends PropertySource { PropertiesConfiguration propertiesConfiguration; public ReloadablePropertySource(String name, PropertiesConfiguration propertiesConfiguration) { super(name); this.propertiesConfiguration = propertiesConfiguration; } public ReloadablePropertySource(String name, String path) { super(StringUtils.hasText(name) ? path : name); try { this.propertiesConfiguration = new PropertiesConfiguration(path); this.propertiesConfiguration.setReloadingStrategy(new FileChangedReloadingStrategy()); } catch (Exception e) { throw new PropertiesException(e); } } @Override public Object getProperty(String s) { return propertiesConfiguration.getProperty(s); } }

Kami telah mengganti metode getProperty untuk mendelegasikannya ke PropertiesConfiguration # getProperty. Karenanya, ini akan memeriksa nilai yang diperbarui dalam interval sesuai dengan penundaan penyegaran kami.

Sekarang, kita akan menambahkan ReloadablePropertySource kita ke sumber properti Environment :

@Configuration public class ReloadablePropertySourceConfig { private ConfigurableEnvironment env; public ReloadablePropertySourceConfig(@Autowired ConfigurableEnvironment env) { this.env = env; } @Bean @ConditionalOnProperty(name = "spring.config.location", matchIfMissing = false) public ReloadablePropertySource reloadablePropertySource(PropertiesConfiguration properties) { ReloadablePropertySource ret = new ReloadablePropertySource("dynamic", properties); MutablePropertySources sources = env.getPropertySources(); sources.addFirst(ret); return ret; } }

Kami telah menambahkan sumber properti baru sebagai item pertama karena kami ingin itu menimpa properti yang ada dengan kunci yang sama.

Mari buat kacang untuk membaca properti dari Lingkungan :

@Component public class EnvironmentConfigBean { private Environment environment; public EnvironmentConfigBean(@Autowired Environment environment) { this.environment = environment; } public String getColor() { return environment.getProperty("application.theme.color"); } }

Jika kita perlu menambahkan sumber properti eksternal yang dapat dimuat ulang lainnya, pertama-tama kita harus menerapkan PropertySourceFactory kustom kita :

public class ReloadablePropertySourceFactory extends DefaultPropertySourceFactory { @Override public PropertySource createPropertySource(String s, EncodedResource encodedResource) throws IOException { Resource internal = encodedResource.getResource(); if (internal instanceof FileSystemResource) return new ReloadablePropertySource(s, ((FileSystemResource) internal) .getPath()); if (internal instanceof FileUrlResource) return new ReloadablePropertySource(s, ((FileUrlResource) internal) .getURL() .getPath()); return super.createPropertySource(s, encodedResource); } }

Kemudian kita dapat membuat anotasi kelas komponen dengan @PropertySource :

@PropertySource(value = "file:path-to-config", factory = ReloadablePropertySourceFactory.class)

3.2. Memuat Ulang Contoh Properti

Lingkungan adalah pilihan yang lebih baik daripada Properti , terutama saat kita perlu memuat ulang properti dari sebuah file. Namun, jika kita membutuhkannya, kita bisa memperluas java.util.Properties :

public class ReloadableProperties extends Properties { private PropertiesConfiguration propertiesConfiguration; public ReloadableProperties(PropertiesConfiguration propertiesConfiguration) throws IOException { super.load(new FileReader(propertiesConfiguration.getFile())); this.propertiesConfiguration = propertiesConfiguration; } @Override public String getProperty(String key) { String val = propertiesConfiguration.getString(key); super.setProperty(key, val); return val; } // other overrides }

Kami telah mengganti getProperty dan kelebihannya, lalu mendelegasikannya ke instance PropertiesConfiguration . Sekarang, kita dapat membuat kacang dari kelas ini dan memasukkannya ke dalam komponen kita.

3.3. Memuat ulang Bean dengan @ConfigurationProperties

Untuk mendapatkan efek yang sama dengan @ConfigurationProperties , kita perlu merekonstruksi instance tersebut.

Namun, Spring hanya akan membuat instance baru dari komponen dengan prototipe atau cakupan permintaan .

Dengan demikian, teknik kami untuk memuat ulang lingkungan juga akan bekerja untuk mereka, tetapi untuk lajang, kami tidak punya pilihan selain menerapkan titik akhir untuk menghancurkan dan membuat ulang kacang, atau untuk menangani pemuatan ulang properti di dalam kacang itu sendiri.

3.4. Memuat ulang Bean dengan @Value

The @value penjelasan menyajikan keterbatasan yang sama sebagai @ConfigurationProperties .

4. Memuat Ulang Properti dengan Aktuator dan Cloud

Spring Actuator provides different endpoints for health, metrics, and configs, but nothing for refreshing beans. Thus, we need Spring Cloud to add a /refresh endpoint to it. This endpoint reloads all property sources of Environment and then publishes an EnvironmentChangeEvent.

Spring Cloud also has introduced @RefreshScope, and we can use it for configuration classes or beans. As a result, the default scope will be refresh instead of singleton.

Using refresh scope, Spring will clear its internal cache of these components on an EnvironmentChangeEvent. Then, on the next access to the bean, a new instance is created.

Let's start by adding spring-boot-starter-actuator to our pom.xml:

 org.springframework.boot spring-boot-starter-actuator 

Then, let's also import spring-cloud-dependencies:

 org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import Greenwich.SR1 

And then we add spring-cloud-starter:

 org.springframework.cloud spring-cloud-starter 

Finally, let's enable the refresh endpoint:

management.endpoints.web.exposure.include=refresh

When we use Spring Cloud, we can set up a Config Server to manage the properties, but we also can continue with our external files. Now, we can handle two other methods of reading properties: @Value and @ConfigurationProperties.

4.1. Refresh Beans with @ConfigurationProperties

Let's show how to use @ConfigurationProperties with @RefreshScope:

@Component @ConfigurationProperties(prefix = "application.theme") @RefreshScope public class ConfigurationPropertiesRefreshConfigBean { private String color; public void setColor(String color) { this.color = color; } //getter and other stuffs }

Our bean is reading “color” property from the root “application.theme” property. Note that we do need the setter method, per Spring's documentation.

After we change the value of “application.theme.color” in our external config file, we can call /refresh, so then, we can get the new value from the bean on next access.

4.2. Refresh Beans with @Value

Let's create our sample component:

@Component @RefreshScope public class ValueRefreshConfigBean { private String color; public ValueRefreshConfigBean(@Value("${application.theme.color}") String color) { this.color = color; } //put getter here }

The process of refreshing is the same as above.

However, it is necessary to note that /refresh won't work for beans with an explicit singleton scope.

5. Conclusion

In this tutorial, we've demonstrated how to reload properties with or without Spring Cloud features. Also, we've shown the pitfalls and exceptions of each of the techniques.

The complete code is available in our GitHub project.