Genindlæser egenskabsfiler om foråret

1. Oversigt

I denne vejledning skal vi vise, hvordan du genindlæser egenskaber i en Spring-applikation.

2. Læseegenskaber om foråret

Vi har forskellige muligheder for at få adgang til ejendomme i foråret:

  1. Miljø - Vi kan injicere Miljø og brug derefter Miljø # getProperty at læse en given ejendom. Miljø indeholder forskellige egenskabskilder som systemegenskaber, -D parametre og application.properties (.yml). Der kan også tilføjes ekstra ejendomskilder til Miljø ved brug af @PropertySource.
  2. Ejendomme - Vi kan indlæse egenskabsfiler i en Ejendomme eksempel, brug den derefter i en bønne ved at ringe egenskaber.get (“ejendom”).
  3. @Værdi - Vi kan injicere en bestemt egenskab i en bønne med @Value ($ {‘ejendom '}) kommentar.
  4. @ConfigurationProperties - vi kan bruge @ConfigurationProperties for at indlæse hierarkiske egenskaber i en bønne.

3. Genindlæsning af egenskaber fra ekstern fil

For at ændre egenskaber i en fil under kørsel, skal vi placere den et eller andet sted uden for krukken. Derefter fortæller vi Spring, hvor det er med kommandolinjenparameter –Spring.config.location = fil: // {sti til fil}. Eller vi kan sætte det i application.properties.

I filbaserede egenskaber bliver vi nødt til at vælge en måde at genindlæse filen på. For eksempel kan vi udvikle et slutpunkt eller en planlægger til at læse filen og opdatere egenskaberne.

Et praktisk bibliotek til at genindlæse filen er Apache's commons-konfiguration. Vi kan bruge Egenskaber Konfiguration med forskellige Genindlæsningstrategi.

Lad os tilføje commons-konfiguration til vores pom.xml:

 commons-configuration commons-configuration 1.10 

Derefter tilføjer vi en metode til at oprette en Egenskaber Konfiguration bønne, som vi bruger senere:

@Bean @ConditionalOnProperty (name = "spring.config.location", matchIfMissing = false) offentlige PropertiesConfiguration egenskaberConfiguration (@Value ("$ {spring.config.location}") streng sti) kaster undtagelse {String filePath = ny fil (sti .substring ("fil:". længde ()). getCanonicalPath (); PropertiesConfiguration configuration = new PropertiesConfiguration (new File (filePath)); configuration.setReloadingStrategy (ny FileChangedReloadingStrategy ()); returkonfiguration; }

I ovenstående kode har vi indstillet FileChangedReloadingStrategy som genindlæsningsstrategi med en standard forsinkelse for opdatering. Det betyder at Egenskaber Konfiguration kontrollerer dato for filændring hvis dens sidste kontrol var før 5000 ms siden.

Vi kan tilpasse forsinkelsen ved hjælp af FileChangedReloadingStrategy # setRefreshDelay.

3.1. Genindlæser Miljø Ejendomme

Hvis vi vil genindlæse de egenskaber, der er indlæst gennem en Miljø for eksempel skal vi udvide PropertySource og brug derefter Egenskaber Konfiguration for at returnere nye værdier fra den eksterne egenskabsfil.

Lad os starte med at udvide PropertySource:

offentlig klasse ReloadablePropertySource udvider PropertySource {PropertiesConfiguration egenskaberConfiguration; public ReloadablePropertySource (String name, PropertiesConfiguration propertiesConfiguration) {super (name); this.propertiesConfiguration = egenskaberConfiguration; } offentlig ReloadablePropertySource (strengnavn, strengsti) {super (StringUtils.hasText (navn)? sti: navn); prøv {this.propertiesConfiguration = nye PropertiesConfiguration (sti); this.propertiesConfiguration.setReloadingStrategy (ny FileChangedReloadingStrategy ()); } fange (Undtagelse e) {smide nye PropertiesException (e); }} @ Override public Object getProperty (String s) {return propertiesConfiguration.getProperty (s); }}

Vi har tilsidesat getProperty metode til at delegere det til PropertiesConfiguration # getProperty. Derfor kontrollerer den for opdaterede værdier i intervaller i henhold til vores opdateringsforsinkelse.

Nu skal vi tilføje vores ReloadablePropertySource til Miljø'S ejendomskilder:

@Configuration public class ReloadablePropertySourceConfig {private ConfigurableEnvironment env; public ReloadablePropertySourceConfig (@Autowired ConfigurableEnvironment env) {this.env = env; } @Bean @ConditionalOnProperty (navn = "spring.config.location", matchIfMissing = false) offentlig ReloadablePropertySource reloadablePropertySource (PropertiesConfiguration egenskaber) {ReloadablePropertySource ret = ny ReloadablePropertySource ("dynamisk", egenskaber); MutablePropertySources sources = env.getPropertySources (); sources.addFirst (ret); returnere ret; }}

Vi har tilføjet den nye ejendomskilde som det første element fordi vi ønsker, at det tilsidesætter enhver eksisterende ejendom med den samme nøgle.

Lad os oprette en bønne at læse en ejendom fra Miljø:

@Komponent offentlig klasse EnvironmentConfigBean {privat miljø miljø; public EnvironmentConfigBean (@Autowired Environment environment) {this.environment = miljø; } public String getColor () {return environment.getProperty ("application.theme.color"); }}

Hvis vi har brug for at tilføje andre genindlæselige eksterne egenskabskilder, skal vi først implementere vores brugerdefinerede PropertySourceFactory:

offentlig klasse ReloadablePropertySourceFactory udvider DefaultPropertySourceFactory {@Override public PropertySource createPropertySource (String s, EncodedResource encodedResource) kaster IOException {Ressource intern = encodedResource.getResource (); hvis (intern forekomst af FileSystemResource) returnerer ny ReloadablePropertySource (s, ((FileSystemResource) intern) .getPath ()); hvis (intern forekomst af FileUrlResource) returnerer ny ReloadablePropertySource (s, ((FileUrlResource) intern) .getURL () .getPath ()); returner super.createPropertySource (s, encodedResource); }}

Derefter kan vi kommentere klassen for en komponent med @PropertySource:

@PropertySource (værdi = "fil: sti til konfiguration", fabrik = ReloadablePropertySourceFactory.class)

3.2. Genindlæsning af egenskabsforekomst

Miljø er et bedre valg end Ejendomme, især når vi har brug for at genindlæse egenskaber fra en fil. Men hvis vi har brug for det, kan vi udvide java.util.Ejendomme:

offentlig klasse ReloadableProperties udvider Egenskaber {private PropertiesConfiguration egenskaberConfiguration; public ReloadableProperties (PropertiesConfiguration propertiesConfiguration) kaster IOException {super.load (ny FileReader (egenskaberConfiguration.getFile ())); this.propertiesConfiguration = egenskaberConfiguration; } @ Override public String getProperty (String key) {String val = propertiesConfiguration.getString (key); super.setProperty (nøgle, val); returval; } // andre tilsidesættelser}

Vi har tilsidesat getProperty og dens overbelastning, derefter delegeret det til en Egenskaber Konfiguration eksempel. Nu kan vi oprette en bønne af denne klasse og indsprøjte den i vores komponenter.

3.3. Genindlæser Bean med @ConfigurationProperties

For at få den samme effekt med @ConfigurationProperties, vi bliver nødt til at rekonstruere forekomsten.

Men Spring opretter kun en ny forekomst af komponenter med prototype eller anmodning rækkevidde.

Således fungerer vores teknik til at genindlæse miljøet også for dem, men for enkeltpersoner har vi intet andet valg end at implementere et slutpunkt til at ødelægge og genskabe bønnen eller til at håndtere ejendomsindlæsningen inde i selve bønnen.

3.4. Genindlæser Bean med @Værdi

Det @Værdi annotation har de samme begrænsninger som @ConfigurationProperties.

4. Genindlæsning af egenskaber fra aktuator og sky

Spring Actuator giver forskellige slutpunkter for sundhed, metrics og configs, men intet til forfriskende bønner. Således har vi brug for Spring Cloud for at tilføje en /Opdater slutpunkt til det. Dette slutpunkt genindlæser alle ejendomskilder til Miljø og derefter offentliggør en EnvironmentChangeEvent.

Spring Cloud har også introduceret @RefreshScope, og vi kan bruge det til konfigurationsklasser eller bønner. Som et resultat vil standardomfanget være Opdater i stedet for singleton.

Ved brug af Opdater omfang, Spring rydder sin interne cache for disse komponenter på en EnvironmentChangeEvent. Derefter oprettes en ny forekomst ved den næste adgang til bønnen.

Lad os starte med at tilføje fjeder-boot-starter-aktuator til vores pom.xml:

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

Lad os så også importere forår-sky-afhængigheder:

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

Og så tilføjer vi spring-cloud-starter:

 org.springframework.cloud spring-cloud-starter 

Lad os endelig aktivere opdateringsendepunktet:

management.endpoints.web.exposure.include = opdatering

Når vi bruger Spring Cloud, kan vi oprette en Config Server til at administrere egenskaberne, men vi kan også fortsætte med vores eksterne filer. Nu kan vi håndtere to andre metoder til læsning af egenskaber: @Værdi og @ConfigurationProperties.

4.1. Opdater bønner med @ConfigurationProperties

Lad os vise, hvordan man bruger @ConfigurationProperties med @RefreshScope:

@Component @ConfigurationProperties (prefix = "application.theme") @RefreshScope offentlig klasse ConfigurationPropertiesRefreshConfigBean {privat Stringfarve; public void setColor (strengfarve) {this.color = color; } // getter og andre ting}

Vores bønne læser “farve" ejendom fra roden "Ansøgning.tema" ejendom. Bemærk, at vi har brug for setter-metoden i henhold til Spring's dokumentation.

Når vi har ændret værdien af ​​“applikation.tema.farve”I vores eksterne konfigurationsfil kan vi ringe /Opdater, så så kan vi få den nye værdi fra bønnen ved næste adgang.

4.2. Opdater bønner med @Værdi

Lad os oprette vores prøvekomponent:

@Komponent @ RefreshScope offentlig klasse ValueRefreshConfigBean {privat strengfarve; offentlig ValueRefreshConfigBean (@Value ("$ {application.theme.color}") Stringfarve) {this.color = color; } // sæt getter her}

Opfriskningsprocessen er den samme som ovenfor.

Det er dog nødvendigt at bemærke det /Opdater fungerer ikke for bønner med en eksplicit singleton rækkevidde.

5. Konklusion

I denne vejledning har vi demonstreret, hvordan man genindlæser egenskaber med eller uden Spring Cloud-funktioner. Vi har også vist faldgruber og undtagelser fra hver af teknikkerne.

Den komplette kode er tilgængelig i vores GitHub-projekt.