In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-01-18 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Development >
Share
Shulou(Shulou.com)06/02 Report--
Today, I will talk to you about how to parse and execute spel expressions based on spring @ Cacheable annotations, which may not be well understood by many people. In order to make you understand better, the editor has summarized the following for you. I hope you can get something from this article.
The @ Cacheable of spring in daily use must be no stranger, the cache implementation based on aop mechanism, and you can choose cacheManager to provide specific cache middleware or in-process cache, similar to @ Transactional's transactionManager, both provide a polymorphic implementation, abstracting the upper layer interface, and the implementation is for the client to choose from. Maybe this is the architecture, abstract design, using interface to expose the mechanism of scalable implementation. Use abstract to integrate similar implementations.
Well, let's take a look at a convenient mechanism provided by @ Cacheable. Everyone has written notes on the logic of taking method parameters for spel expressions, but is it cool to use spel dynamic values for the parameters needed by annotation logic?
Just go to the topic and follow spring's call chain and read the @ Cacheable annotation directly. The alias mechanism of @ Target ({ElementType.TYPE, ElementType.METHOD}) @ Retention (RetentionPolicy.RUNTIME) @ Inherited@Documentedpublic @ interface Cacheable {/ / spring is not discussed here, which is consistent with the role of cacheNames @ AliasFor ("cacheNames") String [] value () default {}; @ AliasFor ("value") String [] cacheNames () default {}. / / Today's protagonist starts with String key () default ""; / / splices key's abstract interface String keyGenerator () default "" / / the person who really does the caching, redis,caffine, or anything else. As for memory or the abstract logic at the top of the process, you don't care. If you use caffine//, you need to consider the consistency of multi-service instances. String cacheManager () default "; String cacheResolver () default"; / / whether you can execute caching is also a condition for spel to cache String condition () default "if the result true is returned." / / if spel returns true, String unless () default "" is not cached; / / whether to execute boolean sync () default false; asynchronously} next, let's see where the key fetch is.
SpringCacheAnnotationParser#parseCacheableAnnotation parsing annotations, fortunately, only one place
No logic is just an assembly.
Continue to follow the above method SpringCacheAnnotationParser#parseCacheAnnotations has come here
@ Nullable private Collection parseCacheAnnotations (DefaultCacheConfig cachingConfig, AnnotatedElement ae, boolean localOnly) {Collection targetClass = getTargetClass (target); CacheOperationSource cacheOperationSource = getCacheOperationSource (); if (cacheOperationSource! = null) {Collection operations = cacheOperationSource.getCacheOperations (method, targetClass) If (! CollectionUtils.isEmpty (operations)) {return execute (invoker, method, new CacheOperationContexts (operations, method, args, target, targetClass));} / / method logic is executed later oh, cache return invoker.invoke () first } look again at the field on the overloaded method execute @ Nullable private Object execute (final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {/ / comment on whether to execute if (contexts.isSynchronized ()) {CacheOperationContext context = contexts.get (CacheableOperation.class). Iterator (). Next () If (isConditionPassing (context, CacheOperationExpressionEvaluator.NO_RESULT)) {Object key = generateKey (context, CacheOperationExpressionEvaluator.NO_RESULT); Cache cache = context.getCaches (). Iterator (). Next (); try {return wrapCacheValue (method, cache.get (key, ()-> unwrapReturnValue (invokeOperation (invoker) } catch (Cache.ValueRetrievalException ex) {/ / Directly propagate ThrowableWrapper from the invoker, / / or potentially also an IllegalArgumentException etc. ReflectionUtils.rethrowRuntimeException (ex.getCause ()) }} else {/ / No caching required, only call the underlying method return invokeOperation (invoker) }} /-synchronous execution cache logic-/ /-the following annotations are executed separately. You can see the sequential cache deletion (before the target method invoke) and execution, cache increase / / add (guess is the first hit cache) between springCache comments. If you miss the cache that stores the empty data first, occupy the cache data in advance, minimize the cache flushing problems caused by concurrent caching, / / cache increase (with data), the real execution of the above two cache additions, cache deletion (after the target method invoke) and / / of course this is pre-invoke or post-execution depends on the beforeInvocation configuration in @ CacheEvict The default false is executed later. If you execute unless before, you will not get the result value / / then spring cache is not delayed double deletion. Oh, high concurrency may exist data expired data re-injection / / Process any early evictions processCacheEvicts (contexts.get (CacheEvictOperation.class), true, CacheOperationExpressionEvaluator.NO_RESULT) / / Check if we have a cached item matching the conditions Cache.ValueWrapper cacheHit = findCachedItem (contexts.get (CacheableOperation.class)); / / Collect puts from any @ Cacheable miss, if no cached item is found List cachePutRequests = new LinkedList (); if (cacheHit = = null) {collectPutRequests (contexts.get (CacheableOperation.class), CacheOperationExpressionEvaluator.NO_RESULT, cachePutRequests) } / / method input parameter resolution for key condition Object cacheValue; / / method result resolution for unless Object returnValue; if (cacheHit! = null & &! hasCachePut (contexts)) {/ / If there are no put requests, just use the cache hit cacheValue = cacheHit.get (); returnValue = wrapCacheValue (method, cacheValue) } else {/ / Invoke the method if we don't have a cache hit returnValue = invokeOperation (invoker); cacheValue = unwrapReturnValue (returnValue);} / / Collect any explicit @ CachePuts collectPutRequests (contexts.get (CachePutOperation.class), cacheValue, cachePutRequests) / / Process any collected put requests, either from @ CachePut or a @ Cacheable miss for (CachePutRequest cachePutRequest: cachePutRequests) {cachePutRequest.apply (cacheValue);} / / Process any late evictions processCacheEvicts (contexts.get (CacheEvictOperation.class), false, cacheValue); return returnValue;}
Instead of delving into the execution logic in detail, let's take a look at the logic for generating key, the private method generateKey
/ / you can see that no key generated will throw an exception. Null private Object generateKey (CacheOperationContext context, @ Nullable Object result) {Object key = context.generateKey (result); if (key = = null) {throw new IllegalArgumentException ("Null key returned for cache operation (maybe you are" + "using named params on classes without debug info?)" + context.metadata.operation) is not allowed. } if (logger.isTraceEnabled ()) {logger.trace ("Computed cache key'" + key + "'for operation" + context.metadata.operation);} return key;} /-continue-/ * Compute the key for the given caching operation. * / @ Nullable protected Object generateKey (@ Nullable Object result) {if (StringUtils.hasText (this.metadata.operation.getKey () {/ / finally see the class in the org.springframework.expression package, one of the core packages of spring. T.T EvaluationContext evaluationContext = createEvaluationContext (result); return evaluator.key (this.metadata.operation.getKey (), this.metadata.methodKey, evaluationContext);} return this.metadata.keyGenerator.generate (this.target, this.metadata.method, this.args);}
You can see that the evaluator used is a member variable of the CacheOperationExpressionEvaluator class, which is generated when the class is loaded. There are methods for generating instances to be parsed, three methods for parsing key condition unless and ConcurrentMap member variables are cached in memory, and all spel expressions annotated by Cache are cached here. The default size of 64 is as follows.
Public EvaluationContext createEvaluationContext (Collection targetClass, Method targetMethod, @ Nullable Object result, @ Nullable BeanFactory beanFactory) {CacheExpressionRootObject rootObject = new CacheExpressionRootObject (caches, method, args, target, targetClass); CacheEvaluationContext evaluationContext = new CacheEvaluationContext (rootObject, targetMethod, args, getParameterNameDiscoverer ()); if (result = = RESULT_UNAVAILABLE) {evaluationContext.addUnavailableVariable (RESULT_VARIABLE) } else if (result! = NO_RESULT) {evaluationContext.setVariable (RESULT_VARIABLE, result);} if (beanFactory! = null) {evaluationContext.setBeanResolver (new BeanFactoryResolver (beanFactory));} return evaluationContext;} @ Nullable public Object key (String keyExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {return get_Expression (this.keyCache, methodKey, keyExpression) .getValue (evalContext) } public boolean condition (String conditionExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {return (Boolean.TRUE.equals (get_Expression (this.conditionCache, methodKey, conditionExpression). GetValue (evalContext, Boolean.class));} public boolean unless (String unlessExpression, AnnotatedElementKey methodKey, EvaluationContext evalContext) {return (Boolean.TRUE.equals (this.unlessCache, methodKey, unlessExpression). GetValue (evalContext, Boolean.class));}
And then return to the desired key.
After reading the above, do you have any further understanding of how to parse and execute spel expressions based on the spring @ Cacheable annotation? If you want to know more knowledge or related content, please follow the industry information channel, thank you for your support.
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.