Memuatkan semula Fail Properties pada Musim Bunga

1. Gambaran keseluruhan

Dalam tutorial ini, kita akan menunjukkan cara memuat semula harta tanah dalam aplikasi Spring.

2. Sifat Membaca pada Musim Bunga

Kami mempunyai pilihan yang berbeza untuk mengakses hartanah pada musim bunga:

  1. Persekitaran - Kita dapat menyuntikkan Persekitaran dan kemudian menggunakan Environment # getProperty untuk membaca harta yang diberikan. Persekitaran mengandungi sumber harta yang berbeza seperti sifat sistem, parameter -D , dan application.properties (.yml) . Juga, sumber harta tambahan boleh ditambahkan ke Alam Sekitar menggunakan @PropertySource .
  2. Properties - Kita dapat memuat fail properti ke dalam instance Properties , kemudian menggunakannya dalam kacang dengan memanggil properties.get ("properti").
  3. @Value - Kami dapat menyuntikkan harta tertentu dalam biji dengan anotasi @Value ($ {'property'}) .
  4. @ConfigurationProperties - kita boleh menggunakan @ConfigurationProperties untuk memuatkan sifat hierarki dalam kacang.

3. Memuatkan semula Properties dari Fail Luaran

Untuk menukar sifat dalam fail semasa runtime, kita harus meletakkan fail tersebut di suatu tempat di luar balang. Kemudian, kita akan memberitahu Spring di mana ia berada dengan baris perintahparameter –spring.config.location = file: // {jalan ke fail} . Atau, kita boleh memasukkannya ke dalam application.properties.

Dalam sifat berdasarkan fail, kita harus memilih cara memuat semula fail. Sebagai contoh, kita dapat mengembangkan titik akhir atau penjadual untuk membaca fail dan mengemas kini sifat.

Satu perpustakaan yang berguna untuk memuat semula fail adalah konfigurasi commons Apache . Kita boleh menggunakan PropertiesConfiguration dengan ReloadingStrategy yang berbeza .

Mari tambahkan konfigurasi commons ke pom.xml kami :

 commons-configuration commons-configuration 1.10 

Kemudian, kami menambahkan kaedah untuk membuat kacang PropertiesConfiguration , yang akan kami gunakan kemudian:

@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 kod di atas, kami telah menetapkan FileChangedReloadingStrategy sebagai strategi memuat semula dengan kelewatan penyegaran semula. Ini bermaksud bahawa PropertiesConfiguration memeriksa tarikh pengubahsuaian fail sekiranya pemeriksaan terakhirnya sebelum 5000ms yang lalu .

Kita boleh menyesuaikan kelewatan menggunakan FileChangedReloadingStrategy # setRefreshDelay.

3.1. Memuatkan Hartanah Persekitaran

Sekiranya kita ingin memuatkan semula sifat yang dimuat melalui contoh Lingkungan , kita harus memperluas PropertySource dan kemudian menggunakan PropertiesConfiguration untuk mengembalikan nilai baru dari fail harta luaran .

Mari mulakan dengan memperluaskan 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 mengatasi kaedah getProperty untuk mendelegasikannya ke PropertiesConfiguration # getProperty. Oleh itu, ia akan memeriksa nilai yang dikemas kini secara berkala mengikut kelewatan penyegaran kami.

Sekarang, kami akan menambahkan ReloadablePropertySource kami ke sumber harta tanah Alam Sekitar :

@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 harta baru sebagai item pertama kerana kami mahu ia menggantikan harta tanah yang ada dengan kunci yang sama.

Mari buat kacang untuk membaca harta tanah dari Alam Sekitar :

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

Sekiranya kita perlu menambahkan sumber sifat luaran yang boleh dimuat semula yang lain, pertama kita harus melaksanakan PropertySourceFactory tersuai 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 memberi penjelasan kelas komponen dengan @PropertySource :

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

3.2. Muat semula Instance Properties

Persekitaran adalah pilihan yang lebih baik daripada Properties , terutama ketika kita perlu memuatkan semula sifat dari fail. Walau bagaimanapun, jika kita memerlukannya, kita dapat 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 mengatasi getProperty dan kelebihannya, kemudian menyerahkannya ke contoh PropertiesConfiguration . Sekarang, kita boleh membuat biji kelas ini dan memasukkannya ke dalam komponen kita.

3.3. Memuat semula Bean dengan @ConfigurationProperties

Untuk mendapatkan kesan yang sama dengan @ConfigurationProperties , kita perlu membina semula contohnya.

Tetapi, Spring hanya akan membuat contoh komponen baru dengan prototaip atau skop permintaan .

Oleh itu, teknik kita untuk memuatkan semula alam sekitar juga akan berfungsi untuk mereka, tetapi untuk orang perseorangan, kita tidak punya pilihan kecuali menerapkan titik akhir untuk memusnahkan dan mencipta kembali kacang, atau untuk menangani penambahan harta tanah di dalam kacang itu sendiri.

3.4. Muat semula Bean dengan @Value

The @value anotasi membentangkan batasan sama seperti @ConfigurationProperties .

4. Memuatkan semula Properties oleh Actuator 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.