Spring Cache kills millions of concurrency in seconds! 90% of projects use this performance optimization secret weapon!

Original Springboot practical case collection Spring Family Bucket practical case source code 2023-10-14 12:34 Published in Xinjiang

Spring Family Bucket practical case source code

Detailed explanation of spring, springboot, springcloud case development

376 original content

No public

Environment: springboot2.6.12 + JSR107 + Ehcache + JPA

Spring Cache is a framework that implements annotation-based caching functionality. It unifies different caching technologies through the CacheManager interface and provides simplification for business operation caching. Specifically, using Spring Cache’s annotations, such as @Cacheable, @CacheEvict and @CachePut, etc., you can implement cache operations very conveniently. , such as cache creation, update and deletion, etc.

Why use Spring Cache? There are mainly the following reasons:

  1. Simplify operations: Through annotations, we can greatly simplify the code we use to operate the cache in our business. There is no need to write a lot of manual cache processing logic, and you can focus more on business logic processing.

  2. Improving performance: Using caching can significantly improve your application’s performance. By storing frequently used data or calculation results in the cache, you can reduce the number of accesses to the database or other external data sources, thereby improving application responsiveness and performance.

  3. Extensibility: Spring Cache provides an abstraction layer that allows us to switch to different cache implementations as needed. For example, we can use Redis as a cache, or we can use EhCache or other caching technologies. This scalability makes our applications more flexible and allows us to choose the most appropriate caching solution based on specific needs.

  4. Security: Spring Cache also provides cache consistency guarantees. For example, in a distributed system, multiple application nodes may have concurrent access to and modification of the same data. Using Spring Cache’s annotations can ensure the consistency of these operations and avoid data inconsistency problems.

In general, the use of Spring Cache can improve the performance and maintainability of the application, making the application more robust and flexible.

Starting from 3.1, the Spring framework provides support for transparently adding cache to Spring applications. Like transaction support, abstract caching allows various caching solutions to be used consistently with minimal impact on the code.
Starting with Spring 4.1, the cache abstraction has been significantly improved by supporting JSR-107 annotations and more customization options.

1. Basic usage

Directly use spring annotations to implement caching

Spring provides the following annotations:

@Cacheable triggers caching mechanism

@CacheEvict triggers cache eviction

@CachePut updates the cache without affecting method execution

@Caching combines multiple caching operations into one method

@CacheConfig class-level shared system common cache-related configurations

<dependency></code><code> <groupId>org.springframework.boot</groupId></code><code> <artifactId>spring-boot-starter-cache</artifactId></code> <code></dependency>

Generally, the corresponding method in Service is to add annotations.

@Service</code><code>publicclass StorageService {<!-- --></code>
<code> @Resource</code><code> private StorageRepository sr;</code><code> </code><code> // The keyGenerator here is the Bean name generated by your custom Key</code><code> @Cacheable(value = {"cache_storage"}, keyGenerator = "storageKey")</code><code> public Storage getStorage(Long id) {<!-- --></code><code> return sr.findById(id).get() ;</code><code> }</code><code>}
@Component("storageKey")</code><code>publicclass StorageKeyGenerator implements KeyGenerator {<!-- --></code>
<code> privatestatic final String KEY_PREFIX = "storage_" ;</code>
<code> @Override</code><code> publicObject generate(Object target, Method method, Object... params) {<!-- --></code><code> StringBuilder sb = new StringBuilder(); </code><code> for (Object param : params) {<!-- --></code><code> sb.append(param) ;</code><code> }</code><code> return KEY_PREFIX + sb.toString() ;</code><code> }</code><code>}

Test access interface

@RestController</code><code>@RequestMapping("/storages")</code><code>public class StorageController {<!-- --></code>
<code> @Resource</code><code> private StorageService storageService ;</code>
<code> @GetMapping("/{id}")</code><code> public Object get(@PathVariable("id") Long id) {<!-- --></code> <code> returnstorageService.getStorage(id) ;</code><code> }</code><code>}

Access the interface for the first time and check the console output of the sql statement:

Picture

When you access the interface again, you find that the console does not output any SQL, indicating that our cache has taken effect (you can also comment out the annotations here to see the effect). Regarding the update cache here, deleting the cache will not be demonstrated. Next is a complete demonstration of the annotations in the JSR107 specification:

Note that we can use SpEL expressions in these comments:

Picture

2. Combination of JSR107 and EhCache

Let’s first take a look at the comparison table between Spring and JSR107 annotations:

Picture

Add dependencies to pom.xml

<dependency></code><code> <groupId>org.springframework.boot</groupId></code><code> <artifactId>spring-boot-starter-cache</artifactId></code> <code></dependency> mysql mysql-connector-java org.ehcache ehcache  javax.cache < artifactId>cache-api

Service class

@Service</code><code>public class StorageService {<!-- --></code>
<code> @Resource</code><code> private StorageRepository sr;</code>
<code> // The @CacheValue description here is the parameter value to be cached. </code><code> @Transactional</code><code> @CachePut(cacheName = "cache_storage", cacheKeyGenerator = JCacheKeyGenerator.class)</code><code> public Storage save(@CacheValue Storage storage) { <!-- --></code><code> returnsr.saveAndFlush(storage) ;</code><code> }</code><code> @CacheResult(cacheName = "cache_storage", cacheKeyGenerator = JCacheKeyGenerator .class)</code><code> publicStoragegetStorage(Long id) {<!-- --></code><code> returnsr.findById(id).get() ;</code><code> }</code>
<code> @Transactional</code><code> @CacheRemove(cacheName = "cache_storage", cacheKeyGenerator = JCacheKeyGenerator.class)</code><code> publicvoidremoveStorage(Long id) {<!-- --></code><code> sr.deleteById(id) ;</code><code> }</code>
<code> @Transactional</code><code> @CachePut(cacheName = "cache_storage", cacheKeyGenerator = JCacheKeyGenerator.class)</code><code> publicStorageupdateStorage(@CacheValue Storage storage) {<!-- -- ></code><code> returnsr.saveAndFlush(storage) ;</code><code> }</code><code>}</code><code>// Note that the cacheKeyGenerator here must all use the same one. </code><code>// I tracked the source code and used the corresponding class name key to find the corresponding cache; I didn’t use the same one at the beginning and it was always incorrect. . 

Source code debugging

Picture

There must be the same cacheKeyGenerator here

Cache Key: JCacheKeyGenerator.java


publicclass JCacheKeyGenerator implements CacheKeyGenerator {<!-- --></code>
<code> private static final String KEY_PREFIX = "storage_" ;</code>
<code> @Override</code><code> public GeneratedCacheKey generateCacheKey(</code><code> CacheKeyInvocationContext<? extends Annotation> cacheKeyInvocationContext) {<!-- --></code><code> CacheInvocationParameter[] params = cacheKeyInvocationContext.getAllParameters() ;</code><code> StringBuilder sb = new StringBuilder() ;</code><code> for (CacheInvocationParameter param : params) {<!-- --></code><code> if (param.getValue() instanceof Storage) {<!-- --></code><code> Storage s = (Storage) param.getValue() ;</code><code> sb.append(s .getId()) ;</code><code> } else {<!-- --></code><code> sb.append((Long)param.getValue()) ;</code><code> }</code><code> }</code><code> return new StorageGeneratedCacheKey(KEY_PREFIX + sb.toString()) ;</code><code> }</code><code>}

application.yml configuration

spring:</code><code> cache:</code><code> cacheNames:</code><code> - cache_storage</code><code> ehcache:</code><code> config: classpath:ehcache.xml

ehcache.xml


<?xml version="1.0" encoding="UTF-8"?></code><code><ehcache xmlns:xsi="http://www.w3.org/2001 /XMLSchema-instance"</code><code> xsi:noNamespaceSchemaLocation="http://ehcache.org/ehcache.xsd"</code><code> updateCheck="false"></code>
<code> <diskStore path="java.io.tmpdir/Tmp_EhCache"/></code><code> <defaultCache eternal="false" maxElementsInMemory="10000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="259200" memoryStoreEvictionPolicy="LRU" /></code><code> <cache name="cache_storage" eternal=" false" maxElementsInMemory="5000" overflowToDisk="false" diskPersistent="false" timeToIdleSeconds="1800" timeToLiveSeconds="1800" memoryStoreEvictionPolicy="LRU" /></code><code></ehcache>

test

Add data first

Picture

Picture

The information with ID 4 was successfully added. We added the @CachePut annotation in the save method in Service. Next, we query the information with ID 4 to see if the console will generate a SQL statement.

Picture

Picture

No SQL statements are added to the console, indicating that the @CachePut added in the save method has taken effect.

Then do the delete operation:

Picture

Picture

The one with ID 4 has been deleted. Let’s check again:

Picture

This shows that after deleting the data, the cache is also deleted. The query statement is generated here.

complete! ! ! !

syntaxbug.com © 2021 All Rights Reserved.