Eigenschappenbestanden opnieuw laden in het voorjaar

1. Overzicht

In deze zelfstudie laten we zien hoe u eigenschappen opnieuw laadt in een Spring-toepassing.

2. Eigenschappen lezen in het voorjaar

We hebben verschillende opties om toegang te krijgen tot eigendommen in het voorjaar:

  1. Milieu - We kunnen injecteren Milieu en gebruik dan Omgeving # getProperty om een ​​bepaalde eigenschap te lezen. Milieu bevat verschillende eigenschapsbronnen zoals systeemeigenschappen, -D parameters, en application.properties (.yml). Ook kunnen extra property-bronnen worden toegevoegd aan het Milieu gebruik makend van @PropertySource.
  2. Eigendommen - We kunnen eigenschappenbestanden laden in een Eigendommen bijvoorbeeld, gebruik het dan in een boon door te bellen properties.get ("eigenschap").
  3. @Waarde - We kunnen een specifieke eigenschap in een boon injecteren met de @Value ($ {‘property '}) annotatie.
  4. @ConfigurationProperties - we kunnen gebruiken @ConfigurationProperties om hiërarchische eigenschappen in een bean te laden.

3. Eigenschappen van extern bestand opnieuw laden

Om eigenschappen in een bestand tijdens runtime te wijzigen, moeten we dat bestand ergens buiten de jar plaatsen. Vervolgens vertellen we Spring waar het is met de opdrachtregelparameter –Spring.config.location = file: // {pad naar bestand}. Of we kunnen het erin stoppen application.properties.

In bestandsgebaseerde eigenschappen moeten we een manier kiezen om het bestand opnieuw te laden. We kunnen bijvoorbeeld een endpoint of planner ontwikkelen om het bestand te lezen en de eigenschappen bij te werken.

Een handige bibliotheek om het bestand opnieuw te laden is die van Apache commons-configuratie. We kunnen gebruiken Eigenschappen Configuratie met verschillende Herlaadstrategie.

Laten we toevoegen commons-configuratie naar onze pom.xml:

 commons-configuratie commons-configuratie 1.10 

Vervolgens voegen we een methode toe om een Eigenschappen Configuratie bean, die we later zullen gebruiken:

@Bean @ConditionalOnProperty (name = "spring.config.location", matchIfMissing = false) public Properties Configuratie propertiesConfiguration (@Value ("$ {spring.config.location}") String path) gooit Uitzondering {String filePath = nieuw bestand (pad .substring ("file:". length ())). getCanonicalPath (); PropertiesConfiguration configuration = nieuwe PropertiesConfiguration (new File (filePath)); configuration.setReloadingStrategy (nieuwe FileChangedReloadingStrategy ()); terugkeer configuratie; }

In de bovenstaande code hebben we ingesteld FileChangedReloadingStrategy als de herlaadstrategie met een standaard vernieuwingsvertraging. Dit betekent dat Eigenschappen Configuratie controleert op de wijzigingsdatum van het bestand als de laatste controle vóór 5000 ms geleden was.

We kunnen de vertraging aanpassen met FileChangedReloadingStrategy # setRefreshDelay.

3.1. Herladen Milieu Eigendommen

Als we de eigenschappen die zijn geladen via een Milieu bijvoorbeeld, we moeten verleng de PropertySource en gebruik dan Eigenschappen Configuratie om nieuwe waarden uit het externe eigenschappenbestand te retourneren.

Laten we beginnen met het uitbreiden van het PropertySource:

openbare klasse ReloadablePropertySource breidt PropertySource uit {PropertiesConfiguration propertiesConfiguration; openbare ReloadablePropertySource (String naam, PropertiesConfiguration propertiesConfiguration) {super (name); this.propertiesConfiguration = propertiesConfiguration; } openbare ReloadablePropertySource (String-naam, String-pad) {super (StringUtils.hasText (naam)? pad: naam); probeer {this.propertiesConfiguration = nieuwe PropertiesConfiguration (pad); this.propertiesConfiguration.setReloadingStrategy (nieuwe FileChangedReloadingStrategy ()); } catch (uitzondering e) {throw nieuwe PropertiesException (e); }} @Override public Object getProperty (String s) {return propertiesConfiguration.getProperty (s); }}

We hebben het getProperty methode om het aan te delegeren PropertiesConfiguration # getProperty. Daarom controleert het in intervallen op bijgewerkte waarden volgens onze vernieuwingsvertraging.

Nu gaan we onze toevoegen HerlaadbarePropertySource naar Milieu‘S eigendomsbronnen:

@Configuration openbare klasse ReloadablePropertySourceConfig {private ConfigurableEnvironment env; openbare ReloadablePropertySourceConfig (@Autowired ConfigurableEnvironment env) {this.env = env; } @Bean @ConditionalOnProperty (name = "spring.config.location", matchIfMissing = false) openbare ReloadablePropertySource reloadablePropertySource (PropertiesConfiguration-eigenschappen) {ReloadablePropertySource ret = nieuwe ReloadablePropertySource ("dynamic", eigenschappen); MutablePropertySources-bronnen = env.getPropertySources (); sources.addFirst (ret); retourneren; }}

We hebben de nieuwe eigenschapsbron als eerste item toegevoegd omdat we willen dat het elke bestaande eigenschap met dezelfde sleutel overschrijft.

Laten we een boon maken om een ​​eigenschap van te lezen Milieu:

@Component openbare klasse EnvironmentConfigBean {privéomgevingomgeving; openbare EnvironmentConfigBean (@Autowired Environment-omgeving) {this.environment = environment; } public String getColor () {return environment.getProperty ("application.theme.color"); }}

Als we andere bronnen voor herlaadbare externe eigenschappen moeten toevoegen, moeten we eerst onze aangepaste PropertySourceFactory:

public class ReloadablePropertySourceFactory breidt DefaultPropertySourceFactory uit {@Override public PropertySource createPropertySource (String s, EncodedResource encodedResource) gooit IOException {Resource internal = encodedResource.getResource (); if (interne instantie van FileSystemResource) retourneert nieuwe ReloadablePropertySource (s, ((FileSystemResource) internal) .getPath ()); if (intern exemplaar van FileUrlResource) retourneert nieuwe ReloadablePropertySource (s, ((FileUrlResource) intern) .getURL () .getPath ()); retourneer super.createPropertySource (s, encodedResource); }}

Dan kunnen we de klasse van een component annoteren met @PropertySource:

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

3.2. Eigenschappen-instantie opnieuw laden

Milieu is een betere keuze dan Eigendommen, vooral als we eigenschappen uit een bestand moeten herladen. Als we het echter nodig hebben, kunnen we het java.util.Properties:

openbare klasse ReloadableProperties breidt Eigenschappen uit {private PropertiesConfiguration propertiesConfiguration; openbare ReloadableProperties (PropertiesConfiguration propertiesConfiguration) gooit IOException {super.load (nieuwe FileReader (propertiesConfiguration.getFile ())); this.propertiesConfiguration = propertiesConfiguration; } @Override public String getProperty (String key) {String val = propertiesConfiguration.getString (key); super.setProperty (key, val); val terug; } // andere overschrijvingen}

We hebben overschreven getProperty en zijn overbelastingen, en vervolgens gedelegeerd aan een Eigenschappen Configuratie voorbeeld. Nu kunnen we een boon van deze klasse maken en deze in onze componenten injecteren.

3.3. Bean herladen met @ConfigurationProperties

Om hetzelfde effect mee te krijgen @ConfigurationProperties, moeten we de instantie reconstrueren.

Maar Spring zal alleen een nieuw exemplaar van componenten maken met voorlopig ontwerp of verzoek reikwijdte.

Onze techniek om de omgeving opnieuw te laden zal dus ook voor hen werken, maar voor singletons hebben we geen andere keus dan een eindpunt te implementeren om de boon te vernietigen en opnieuw te creëren, of om het herladen van de eigenschap in de boon zelf af te handelen.

3.4. Bean herladen met @Waarde

De @Waarde annotatie heeft dezelfde beperkingen als @ConfigurationProperties.

4. Eigenschappen opnieuw laden via Actuator en Cloud

Spring Actuator biedt verschillende eindpunten voor gezondheid, statistieken en configuraties, maar niets voor het vernieuwen van bonen. We hebben dus Spring Cloud nodig om een / vernieuwen eindpunt ernaar toe. Dit eindpunt laadt alle property-bronnen van Milieu en publiceert vervolgens een EnvironmentChangeEvent.

Spring Cloud heeft ook geïntroduceerd @RefreshScope, en we kunnen het gebruiken voor configuratieklassen of bonen. Als gevolg hiervan is het standaardbereik vernieuwen in plaats van singleton.

Gebruik makend van vernieuwen scope, zal Spring zijn interne cache van deze componenten wissen op een EnvironmentChangeEvent. Vervolgens wordt bij de volgende toegang tot de bean een nieuw exemplaar gemaakt.

Laten we beginnen met toevoegen veer-boot-starter-actuator naar onze pom.xml:

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

Laten we dan ook importeren spring-cloud-afhankelijkheden:

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

En dan voegen we toe spring-cloud-starter:

 org.springframework.cloud spring-cloud-starter 

Laten we tot slot het vernieuwings-eindpunt inschakelen:

management.endpoints.web.exposure.include = vernieuwen

Wanneer we Spring Cloud gebruiken, kunnen we een Config Server opzetten om de eigenschappen te beheren, maar we kunnen ook doorgaan met onze externe bestanden. Nu kunnen we twee andere methoden gebruiken om eigenschappen te lezen: @Waarde en @ConfigurationProperties.

4.1. Ververs Bonen met @ConfigurationProperties

Laten we laten zien hoe u @ConfigurationProperties met @RefreshScope:

@Component @ConfigurationProperties (prefix = "application.theme") @RefreshScope openbare klasse ConfigurationPropertiesRefreshConfigBean {privé String-kleur; public void setColor (String kleur) {this.color = color; } // getter en andere dingen}

Onze boon leest "kleur" property van de root "toepassing.thema" eigendom. Merk op dat we de setter-methode nodig hebben, volgens de documentatie van Spring.

Nadat we de waarde van 'application.theme.color”In ons externe configuratiebestand, kunnen we bellen / vernieuwen, dus dan kunnen we de nieuwe waarde van de boon krijgen bij de volgende toegang.

4.2. Ververs Bonen met @Waarde

Laten we onze voorbeeldcomponent maken:

@Component @RefreshScope openbare klasse ValueRefreshConfigBean {privé String-kleur; openbare ValueRefreshConfigBean (@Value ("$ {application.theme.color}") String kleur) {this.color = color; } // zet getter hier}

Het verfrissingsproces is hetzelfde als hierboven.

Het is echter noodzakelijk om dat op te merken / vernieuwen werkt niet voor bonen met een expliciete singleton reikwijdte.

5. Conclusie

In deze zelfstudie hebben we laten zien hoe u eigenschappen opnieuw laadt met of zonder Spring Cloud-functies. We hebben ook de valkuilen en uitzonderingen van elk van de technieken laten zien.

De volledige code is beschikbaar in ons GitHub-project.