Implementation principle of springboot @Cacheable

Posted by plasmagames on Thu, 03 Feb 2022 12:24:48 +0100

1. Use of springboot cache

  a.pom introduce jar spring-boot-starter-cache
  b.Add annotation to startup class@EnableCaching
  c.Add annotations to the methods that need to be cached @Cacheable(cacheNames = "com:xxx",key = "''+#id")

Figure 1

2. Implementation principle of cacheable

What is the implementation principle? The first reaction in my mind should of course be the famous concepts of AOP, dynamic agent and Interceptor. How can we verify them?

3. Implementation principle verification of cacheable

From the entry break point of calling the method getById to the CglibAopProxy dynamic proxy, you can see the key code to obtain the Interceptor call chain

# DynamicAdvisedInterceptor.intercept method
 List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

The specific Interceptor is shown in the figure below. Sure enough, there is a CacheInterceptor related to Cache. Since then, we have verified our guess in the second part
Figure 2

4. How is the principle realized?

a. Continue with the source code in step 3. Go inside to see where the Interceptor is obtained. Go down to see that the cached Interceptor is obtained through the advisor, as shown in Figure 3 below

b. Go on to locate the specific class beanfactory cacheoperationsourceadvisor, as shown in Figure 4 below

c. The latter logic is to obtain the configured Interceptor from the advisors. When we see the BeanFactory related operations, we must think of the related operations initialized when the application is started. Therefore, we should think of adding @ EnableCaching annotation to the startup class in step 1, so we go back to the starting position and track down.

d. We found an @ import ({cacheingconfigurationselector. Class}) annotation under @ enablecaching. We followed it to selectImports - > getProxyImports. In the getProxyImports method, we saw the following code Figure 5

e. Continue to the ProxyCachingConfiguration class and finally see whether the beanfactory cacheoperationsourceadvisor is associated with step 4? Is it associated with CacheInterceptor? Figure 6

5. How is the @ Cacheable annotation of the specific method associated with the CacheInterceptor?

Let's continue from Figure 4

# First step
 Located CacheOperationSourcePointcut.matches()method
#Step two
AbstractFallbackCacheOperationSource.getCacheOperations()method
#Step 3: finally, the key is obtained from attributeCache
public Collection<CacheOperation> getCacheOperations(Method method, @Nullable Class<?> targetClass) {
        ....Omit non critical code
        # Get key from cache
        Object cacheKey = this.getCacheKey(method, targetClass);
          Collection<CacheOperation> cached = (Collection)this.attributeCache.get(cacheKey);
          if (cached != null) {
              return cached != NULL_CACHING_ATTRIBUTE ? cached : null;
          } else {
              # If there is no key in the cache, judge whether the method has CacheOperation
              Collection<CacheOperation> cacheOps = this.computeCacheOperations(method, targetClass);
              if (cacheOps != null) {
                  if (this.logger.isTraceEnabled()) {
                      this.logger.trace("Adding cacheable method '" + method.getName() + "' with attribute: " + cacheOps);
                  }
                  # CacheOperation operation exists, put it into cache
                  this.attributeCache.put(cacheKey, cacheOps);
              } else {
                  # CacheOperation operation exists, which is stored in the empty collection
                  this.attributeCache.put(cacheKey, NULL_CACHING_ATTRIBUTE);
              }

              return cacheOps;
          }
    }

Track this Computecacheoperations() method, locate parseCacheAnnotations() method, and finally see Cacheable, CacheEvict, CachePut and other related annotations. Since then, all the answers have been solved.

# SpringCacheAnnotationParser.parseCacheAnnotations() method
Collection<CacheOperation> ops = new ArrayList(1);
            anns.stream().filter((ann) -> {
                return ann instanceof Cacheable;
            }).forEach((ann) -> {
                ops.add(this.parseCacheableAnnotation(ae, cachingConfig, (Cacheable)ann));
            });
            anns.stream().filter((ann) -> {
                return ann instanceof CacheEvict;
            }).forEach((ann) -> {
                ops.add(this.parseEvictAnnotation(ae, cachingConfig, (CacheEvict)ann));
            });
            anns.stream().filter((ann) -> {
                return ann instanceof CachePut;
            }).forEach((ann) -> {
                ops.add(this.parsePutAnnotation(ae, cachingConfig, (CachePut)ann));
            });
            anns.stream().filter((ann) -> {
                return ann instanceof Caching;
            }).forEach((ann) -> {
                this.parseCachingAnnotation(ae, cachingConfig, (Caching)ann, ops);
            });
            return ops;

6. Summary

Previously, we combed the implementation principle of @ Cachebale under springboot from hypothesis to verification, and then summarized the major steps from top to bottom.
a.EnableCaching initializes the advisor and registers the interceptor interceptor

b. when the container is initialized, put the methods with Cache related annotations into attributeCache and Cache them

c. The method with Cache enhances the weaving interceptor through Cglib dynamic agent

d. locate the cacheinterceptor in combination with the initialization information of a and b to realize the functions of adding, deleting and changing the cache

Topics: Spring Spring Boot Back-end Cache