If you have used spring Cache, you must be familiar with this configuration and code:
<cache:annotation-driven cache-manager= "cacheManager" proxy-target- class = "true" order= "1" /> |
@Cacheable (value = "3600" , key = "i‘m a cache key" ) public List<Object> getData(){} |
The above two pieces of code, XML is to enable the cache annotation annotations and register a CacheManager, the second code in GetData will go to the cache first, if the cache does not execute GetData the true logic.
So today's "go to Science" is about how spring does the automatic caching of method levels with just a single XML configuration and one annotation.
The best part of our spring is that the IOC container for the bean management, can be said to be the cornerstone of spring, then the drawing of the wind, to the beginning of our discussion today is the spring at the start of the various tags in the XML parsing, such as the corresponding <cache: Annotation-driven> tags, is responsible for parsing is the Annotationdrivencachebeandefinitionparser.parse method, the code looks very simple, according to the mode attribute registered Advisor Component:
Expand the original code
We look at the default Mode=proxy today, enter the method, found that the method registered three beans into the context, respectively, is Cacheoperationsource, Cacheinterceptor and Beanfactorycacheoperationsourceadvisor.
Familiar with the principles of AOP see Interceptor and advisor will generally understand the majority, and they have a common attribute cacheoperationsources, The implementation class is Org.springframework.cache.annotation.AnnotationCacheOperationSource.
Here we first meow two eyes these two classes, first see beanfactorycacheoperationsourceadvisor, inside there is a called Cacheoperationsourcepointcut pointcut, Used to match whether the method needs to go to the interceptor. Get cacheoperation by calling the cacheoperationsources.getcacheoperations that you injected before, the code is as follows:
Expand the original code
This will only be blocked by the Cacheoperationsourcepointcut matching method and cached by Attributecache.
Then look at the Cacheinterceptor class, first see the Inheritance structure:
This class is simple, just rewriting the methodinterceptor Invoke method:
Expand the original code
The next step is to call Cacheaspectsupport
protected
Object execute(CacheOperationInvoker invoker, Object target, Method method, Object[] args) {
// check whether aspect is enabled
// to cope with cases where the AJ is pulled in automatically
if
(
this
.initialized) {
Class<?> targetClass = getTargetClass(target);
Collection<CacheOperation> operations = getCacheOperationSource().getCacheOperations(method, targetClass);
if
(!CollectionUtils.isEmpty(operations)) {
return
execute(invoker,
new
CacheOperationContexts(operations, method, args, target, targetClass));
}
}
return
invoker.invoke();
}
|
Where execute is called after obtaining cacheoperations according to Getcacheoperations is the key, Where Getcacheoperationsource is the cacheoperationsources in the bean that was said before, That is Org.springframework.cache.annotation.AnnotationCacheOperationSource, which is responsible for three tag calls: @Cacheable, @ Cacheput and @cacheevict.
Here's a look at the code for the Execute method:
private
Object execute(CacheOperationInvoker invoker, CacheOperationContexts contexts) {
// Process any early evictions
processCacheEvicts(contexts.get(CacheEvictOperation.
class
),
true
, ExpressionEvaluator.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<CachePutRequest> cachePutRequests =
new LinkedList<CachePutRequest>();
if
(cacheHit ==
null
) {
collectPutRequests(contexts.get(CacheableOperation.
class
), ExpressionEvaluator.NO_RESULT, cachePutRequests);
}
Cache.ValueWrapper result =
null
;
// If there are no put requests, just use the cache hit
if
(cachePutRequests.isEmpty() && !hasCachePut(contexts)) {
result = cacheHit;
}
// Invoke the method if don‘t have a cache hit
if
(result ==
null
) {
result =
new
SimpleValueWrapper(invokeOperation(invoker));
}
// Collect any explicit @CachePuts
collectPutRequests(contexts.get(CachePutOperation.
class
), result.get(), cachePutRequests);
// Process any collected put requests, either from @CachePut or a @Cacheable miss
for
(CachePutRequest cachePutRequest : cachePutRequests) {
cachePutRequest.apply(result.get());
}
// Process any late evictions
processCacheEvicts(contexts.get(CacheEvictOperation.
class
),
false
, result.get());
return
result.get();
}
|
This code seems to be relatively "simple", but also the core implementation of the spring cache logic, according to the annotations performed before and after the method of the cache operation, note that the invalid operation is divided into early evictions and late evictions, Corresponds to the Beforeinvocation attribute in the label @cacheevict. Since then, the logic for Spring cache has been executed.
There are two more points to note.
- The above implementation is implemented by proxy, then the method of the object is an internal call (that is, this reference) instead of an external reference, it will cause the proxy to fail, that is, the annotation invalidation.
- Non-public method ibid.
- The @CacheEvict tag does not empty the cache of the method that throws the exception by setting Beforeinvocation to True, that is, before the method executes
Last words:
This article does not speak about the principles of spring's AOP implementation and the details of the spring cache.
Reference:
Https://docs.spring.io/spring/docs/current/spring-framework-reference/html/cache.html
https://www.ibm.com/developerworks/cn/opensource/os-cn-spring-cache/
Http://www.cnblogs.com/chanedi/p/4552555.html
Originally published on October 16, 2015
The implementation principle of @Cacheable