Een gids voor caching in het voorjaar

1. De cache-abstractie?

In dit artikel laten we zien hoe gebruik de Caching Abstraction in Spring - en in het algemeen de prestaties van uw systeem verbeteren.

We zullen eenvoudige caching inschakelen voor enkele praktijkvoorbeelden van methoden en we zullen bespreken hoe we de prestaties van deze aanroepen praktisch kunnen verbeteren door middel van slim cachebeheer.

2. Aan de slag

De kerncaching-abstractie die door Spring wordt geboden, bevindt zich in de lente-context module. Dus als u Maven gebruikt, is onze pom.xml moet de volgende afhankelijkheid bevatten:

 org.springframework spring-context 5.2.8.RELEASE 

Interessant is dat er een andere module is genaamd lente-context-ondersteuning, die bovenop de lente-context module en biedt nog een paar CacheManagers ondersteund door EhCache of Caffeine. Als je deze gaat gebruiken als je cache-opslag, gebruik dan de spring-context-ondersteuning module in plaats daarvan:

 org.springframework spring-context-support 5.2.8.RELEASE 

Sinds de spring-context-ondersteuning module is tijdelijk afhankelijk van de lente-context module, is er geen aparte afhankelijkheidsverklaring nodig voor het lente-context.

2.1. Spring Boot

Als u een Spring Boot-gebruiker bent, gebruikt u de spring-boot-starter-cache starterspakket om eenvoudig de caching-afhankelijkheden toe te voegen:

 org.springframework.boot spring-boot-starter-cache 2.3.3.RELEASE 

Onder de motorkap brengt de starter de spring-context-ondersteuning module.

3. Schakel Caching in

Om caching mogelijk te maken, maakt Spring goed gebruik van annotaties, net zoals het inschakelen van een andere functie op configuratieniveau in het framework.

De cachefunctie kan declaratief worden ingeschakeld door simpelweg de extensie @EnableCaching annotatie bij een van de configuratieklassen:

@Configuration @EnableCaching openbare klasse CachingConfig {@Bean openbare CacheManager cacheManager () {retourneer nieuwe ConcurrentMapCacheManager ("adressen"); }}

U kunt natuurlijk schakel cachebeheer met XML in configuratie ook:

Opmerking: Nadat we caching hebben ingeschakeld - voor de minimale installatie - moeten we een cacheManager.

3.1. Spring Boot gebruiken

Bij gebruik van Spring Boot is de loutere aanwezigheid van het starterspakket op het klassenpad naast het EnableCaching annotatie zou hetzelfde registreren ConcurrentMapCacheManager. Er is dus geen aparte bonenverklaring nodig.

We kunnen ook het automatisch geconfigureerde CacheManager met een of meer CacheManagerCustomizer bonen:

@Component openbare klasse SimpleCacheCustomizer implementeert CacheManagerCustomizer {@Override openbare leegte aanpassen (ConcurrentMapCacheManager cacheManager) {cacheManager.setCacheNames (asList ("gebruikers", "transacties")); }}

De CacheAutoConfiguration auto-configuratie pikt deze aanpassers op en past ze toe op de huidige CacheManager vóór de volledige initialisatie.

4. Gebruik caching met annotaties

Zodra we caching hebben ingeschakeld, is de volgende stap om het cachegedrag te binden aan de methoden met declaratieve annotaties.

4.1. @Cachebaar

De eenvoudigste manier om cachegedrag voor een methode in te schakelen, is door deze af te bakenen met @Cacheable en parametreer het met de naam van de cache waar de resultaten zouden worden opgeslagen:

@Cacheable ("adressen") openbare tekenreeks getAddress (klant klant) {...} 

De getAddress () call zal eerst de cache controleren adressen voordat u de methode daadwerkelijk aanroept en vervolgens het resultaat in het cachegeheugen plaatst.

Hoewel in de meeste gevallen één cache voldoende is, ondersteunt het Spring-framework ook meerdere caches die als parameters kunnen worden doorgegeven:

@Cacheable ({"adressen", "directory"}) openbare tekenreeks getAddress (klant klant) {...}

In dit geval, als een van de caches het vereiste resultaat bevat, wordt het resultaat geretourneerd en wordt de methode niet aangeroepen.

4.2. @CacheEvict

Nu, wat zou het probleem zijn met het maken van alle methoden @Cacheable?

Het probleem is de grootte - we willen de cache niet vullen met waarden die we niet vaak nodig hebben. Caches kunnen behoorlijk groot en snel worden, en we kunnen veel oude of ongebruikte gegevens vasthouden.

De @CacheEvict annotatie wordt gebruikt om het verwijderen van een of meer / alle waarden aan te geven - zodat nieuwe waarden weer in de cache kunnen worden geladen:

@CacheEvict (value = "adressen", allEntries = true) public String getAddress (klant klant) {...}

Hier gebruiken we de aanvullende parameter alle inschrijvingen in combinatie met de te legen cache - om alle vermeldingen in de cache te wissen adressen en bereid het voor op nieuwe gegevens.

4.3. @CachePut

Terwijl @CacheEvict vermindert de overhead van het opzoeken van vermeldingen in een grote cache door verouderde en ongebruikte vermeldingen te verwijderen, idealiter wilt u dat vermijd het verwijderen van te veel gegevens uit de cache.

In plaats daarvan wilt u de vermeldingen selectief en intelligent bijwerken wanneer ze worden gewijzigd.

Met de @CachePut annotatie, kunt u de inhoud van de cache bijwerken zonder de uitvoering van de methode te verstoren. Dat wil zeggen, de methode wordt altijd uitgevoerd en het resultaat wordt in de cache opgeslagen.

@CachePut (value = "adressen") public String getAddress (klant klant) {...}

Het verschil tussen @Cacheable en @CachePut is dat @Cacheable zullen sla de methode over, terwijl @CachePut zullen voer de methode daadwerkelijk uit en zet de resultaten vervolgens in de cache.

4.4. @Caching

Wat als u meerdere annotaties van hetzelfde type wilt gebruiken om een ​​methode in het cachegeheugen te plaatsen? Kijk naar het onjuiste voorbeeld hieronder:

@CacheEvict ("adressen") @CacheEvict (waarde = "directory", key = klantnaam) public String getAddress (klant klant) {...}

De bovenstaande code zou niet kunnen worden gecompileerd omdat Java niet toestaat dat meerdere annotaties van hetzelfde type voor een bepaalde methode worden gedeclareerd.

De oplossing voor het bovenstaande probleem is:

@Caching (evict = {@CacheEvict ("adressen"), @CacheEvict (value = "directory", key = "# customer.name")}) public String getAddress (klant-klant) {...}

Zoals weergegeven in het bovenstaande codefragment, kunt u groepeer meerdere caching-annotaties met @Caching, en gebruik het om uw eigen aangepaste cachelogica te implementeren.

4.5. @CacheConfig

Met de @CacheConfig annotatie, dat kan stroomlijn een deel van de cacheconfiguratie op één plek - op klassenniveau - zodat u dingen niet meerdere keren hoeft aan te geven:

@CacheConfig (cacheNames = {"adressen"}) public class CustomerDataService {@Cacheable public String getAddress (klant klant) {...}

5. Voorwaardelijke caching

Soms werkt caching niet in alle situaties goed voor een methode.

Bijvoorbeeld - ons voorbeeld hergebruiken uit de @CachePut annotatie - dit zal zowel de methode uitvoeren als de resultaten elke keer in de cache opslaan:

@CachePut (value = "adressen") public String getAddress (klant klant) {...} 

5.1. Conditieparameter

Nu - als we meer controle willen over wanneer de annotatie actief is - @CachePut kan worden geparametriseerd met een voorwaardeparameter die een SpEL-expressie nodig heeft om ervoor te zorgen dat de resultaten in de cache worden opgeslagen op basis van het evalueren van die expressie:

@CachePut (waarde = "adressen", voorwaarde = "# klant.naam == 'Tom'") openbare tekenreeks getAddress (klant klant) {...}

5.2. Tenzij Parameter

We kunnen ook de caching regelen gebaseerd op de output van de methode in plaats van de input - via de tenzij parameter:

@CachePut (value = "adressen", tenzij = "# result.length () <64") public String getAddress (klant klant) {...}

De bovenstaande annotatie zou adressen in cache opslaan, tenzij ze korter zijn dan 64 tekens.

Het is belangrijk om te weten dat de staat en tenzij parameters kunnen worden gebruikt in combinatie met alle caching-annotaties.

Dit soort voorwaardelijke caching kan erg handig zijn voor het beheren van grote resultaten en het aanpassen van gedrag op basis van invoerparameters in plaats van een algemeen gedrag af te dwingen voor alle bewerkingen.

6. Declaratieve XML-gebaseerde caching

Als u geen toegang heeft tot de broncode van uw applicatie of het cachegedrag extern wilt injecteren, kunt u ook declaratieve XML-gebaseerde caching gebruiken.

Hier is onze XML-configuratie:

7. De op Java gebaseerde caching

En hier is de equivalente Java-configuratie:

@Configuration @EnableCaching openbare klasse CachingConfig {@Bean openbare CacheManager cacheManager () {SimpleCacheManager cacheManager = nieuwe SimpleCacheManager (); cacheManager.setCaches (Arrays.asList (nieuwe ConcurrentMapCache ("directory"), nieuwe ConcurrentMapCache ("adressen"))); terug cacheManager; }}

En hier is onze CustomerDataService:

@Component openbare klasse CustomerDataService {@Cacheable (waarde = "adressen", key = "# klant.naam") openbare tekenreeks getAddress (klantklant) {return customer.getAddress (); }}

8. Samenvatting

In dit artikel hebben we de basisprincipes van Caching in Spring besproken en hoe je die abstractie goed kunt gebruiken met annotaties.

De volledige implementatie van dit artikel is te vinden in het GitHub-project.