Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/*
* Copyright 2002-2016 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.springframework.cache.interceptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.aop.framework.AopProxyUtils;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.NoSuchBeanDefinitionException;
import org.springframework.beans.factory.NoUniqueBeanDefinitionException;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.BeanFactoryAnnotationUtils;
import org.springframework.cache.Cache;
import org.springframework.cache.CacheManager;
import org.springframework.context.ApplicationContext;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.expression.EvaluationContext;
import org.springframework.lang.UsesJava8;
import org.springframework.util.Assert;
import org.springframework.util.ClassUtils;
import org.springframework.util.CollectionUtils;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
/**
* Base class for caching aspects, such as the {@link CacheInterceptor}
* or an AspectJ aspect.
*
*
This enables the underlying Spring caching infrastructure to be
* used easily to implement an aspect for any aspect system.
*
*
Subclasses are responsible for calling methods in this class in
* the correct order.
*
*
Uses the Strategy design pattern. A {@link CacheResolver}
* implementation will resolve the actual cache(s) to use, and a
* {@link CacheOperationSource} is used for determining caching
* operations.
*
*
A cache aspect is serializable if its {@code CacheResolver} and
* {@code CacheOperationSource} are serializable.
*
* @author Costin Leau
* @author Juergen Hoeller
* @author Chris Beams
* @author Phillip Webb
* @author Sam Brannen
* @author Stephane Nicoll
* @since 3.1
*/
public abstract class CacheAspectSupport extends AbstractCacheInvoker
implements BeanFactoryAware, InitializingBean, SmartInitializingSingleton {
private static Class javaUtilOptionalClass = null;
static {
try {
javaUtilOptionalClass =
ClassUtils.forName("java.util.Optional", CacheAspectSupport.class.getClassLoader());
}
catch (ClassNotFoundException ex) {
// Java 8 not available - Optional references simply not supported then.
}
}
protected final Log logger = LogFactory.getLog(getClass());
private final Map metadataCache =
new ConcurrentHashMap(1024);
private final CacheOperationExpressionEvaluator evaluator = new CacheOperationExpressionEvaluator();
private CacheOperationSource cacheOperationSource;
private KeyGenerator keyGenerator = new SimpleKeyGenerator();
private CacheResolver cacheResolver;
private BeanFactory beanFactory;
private boolean initialized = false;
/**
* Set one or more cache operation sources which are used to find the cache
* attributes. If more than one source is provided, they will be aggregated
* using a {@link CompositeCacheOperationSource}.
*/
public void setCacheOperationSources(CacheOperationSource... cacheOperationSources) {
Assert.notEmpty(cacheOperationSources, "At least 1 CacheOperationSource needs to be specified");
this.cacheOperationSource = (cacheOperationSources.length > 1 ?
new CompositeCacheOperationSource(cacheOperationSources) : cacheOperationSources[0]);
}
/**
* Return the CacheOperationSource for this cache aspect.
*/
public CacheOperationSource getCacheOperationSource() {
return this.cacheOperationSource;
}
/**
* Set the default {@link KeyGenerator} that this cache aspect should delegate to
* if no specific key generator has been set for the operation.
*
The default is a {@link SimpleKeyGenerator}
*/
public void setKeyGenerator(KeyGenerator keyGenerator) {
this.keyGenerator = keyGenerator;
}
/**
* Return the default {@link KeyGenerator} that this cache aspect delegates to.
*/
public KeyGenerator getKeyGenerator() {
return this.keyGenerator;
}
/**
* Set the {@link CacheManager} to use to create a default {@link CacheResolver}.
* Replace the current {@link CacheResolver}, if any.
* @see #setCacheResolver(CacheResolver)
* @see SimpleCacheResolver
*/
public void setCacheManager(CacheManager cacheManager) {
this.cacheResolver = new SimpleCacheResolver(cacheManager);
}
/**
* Set the default {@link CacheResolver} that this cache aspect should delegate
* to if no specific cache resolver has been set for the operation.
*
The default resolver resolves the caches against their names and the
* default cache manager.
* @see #setCacheManager(org.springframework.cache.CacheManager)
* @see SimpleCacheResolver
*/
public void setCacheResolver(CacheResolver cacheResolver) {
Assert.notNull(cacheResolver, "CacheResolver must not be null");
this.cacheResolver = cacheResolver;
}
/**
* Return the default {@link CacheResolver} that this cache aspect delegates to.
*/
public CacheResolver getCacheResolver() {
return this.cacheResolver;
}
/**
* Set the containing {@link BeanFactory} for {@link CacheManager} and other
* service lookups.
* @since 4.3
*/
@Override
public void setBeanFactory(BeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
/**
* @deprecated as of 4.3, in favor of {@link #setBeanFactory}
*/
@Deprecated
public void setApplicationContext(ApplicationContext applicationContext) {
this.beanFactory = applicationContext;
}
@Override
public void afterPropertiesSet() {
Assert.state(getCacheOperationSource() != null, "The 'cacheOperationSources' property is required: " +
"If there are no cacheable methods, then don't use a cache aspect.");
Assert.state(getErrorHandler() != null, "The 'errorHandler' property is required");
}
@Override
public void afterSingletonsInstantiated() {
if (getCacheResolver() == null) {
// Lazily initialize cache resolver via default cache manager...
try {
setCacheManager(this.beanFactory.getBean(CacheManager.class));
}
catch (NoUniqueBeanDefinitionException ex) {
throw new IllegalStateException("No CacheResolver specified, and no unique bean of type " +
"CacheManager found. Mark one as primary (or give it the name 'cacheManager') or " +
"declare a specific CacheManager to use, that serves as the default one.");
}
catch (NoSuchBeanDefinitionException ex) {
throw new IllegalStateException("No CacheResolver specified, and no bean of type CacheManager found. " +
"Register a CacheManager bean or remove the @EnableCaching annotation from your configuration.");
}
}
this.initialized = true;
}
/**
* Convenience method to return a String representation of this Method
* for use in logging. Can be overridden in subclasses to provide a
* different identifier for the given method.
* @param method the method we're interested in
* @param targetClass class the method is on
* @return log message identifying this method
* @see org.springframework.util.ClassUtils#getQualifiedMethodName
*/
protected String methodIdentification(Method method, Class targetClass) {
Method specificMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
return ClassUtils.getQualifiedMethodName(specificMethod);
}
protected Collection getCaches(
CacheOperationInvocationContext context, CacheResolver cacheResolver) {
Collection caches = cacheResolver.resolveCaches(context);
if (caches.isEmpty()) {
throw new IllegalStateException("No cache could be resolved for '" +
context.getOperation() + "' using resolver '" + cacheResolver +
"'. At least one cache should be provided per cache operation.");
}
return caches;
}
protected CacheOperationContext getOperationContext(
CacheOperation operation, Method method, Object[] args, Object target, Class targetClass) {
CacheOperationMetadata metadata = getCacheOperationMetadata(operation, method, targetClass);
return new CacheOperationContext(metadata, args, target);
}
/**
* Return the {@link CacheOperationMetadata} for the specified operation.
*
Resolve the {@link CacheResolver} and the {@link KeyGenerator} to be
* used for the operation.
* @param operation the operation
* @param method the method on which the operation is invoked
* @param targetClass the target type
* @return the resolved metadata for the operation
*/
protected CacheOperationMetadata getCacheOperationMetadata(
CacheOperation operation, Method method, Class targetClass) {
CacheOperationCacheKey cacheKey = new CacheOperationCacheKey(operation, method, targetClass);
CacheOperationMetadata metadata = this.metadataCache.get(cacheKey);
if (metadata == null) {
KeyGenerator operationKeyGenerator;
if (StringUtils.hasText(operation.getKeyGenerator())) {
operationKeyGenerator = getBean(operation.getKeyGenerator(), KeyGenerator.class);
}
else {
operationKeyGenerator = getKeyGenerator();
}
CacheResolver operationCacheResolver;
if (StringUtils.hasText(operation.getCacheResolver())) {
operationCacheResolver = getBean(operation.getCacheResolver(), CacheResolver.class);
}
else if (StringUtils.hasText(operation.getCacheManager())) {
CacheManager cacheManager = getBean(operation.getCacheManager(), CacheManager.class);
operationCacheResolver = new SimpleCacheResolver(cacheManager);
}
else {
operationCacheResolver = getCacheResolver();
}
metadata = new CacheOperationMetadata(operation, method, targetClass,
operationKeyGenerator, operationCacheResolver);
this.metadataCache.put(cacheKey, metadata);
}
return metadata;
}
/**
* Return a bean with the specified name and type. Used to resolve services that
* are referenced by name in a {@link CacheOperation}.
* @param beanName the name of the bean, as defined by the operation
* @param expectedType type for the bean
* @return the bean matching that name
* @throws org.springframework.beans.factory.NoSuchBeanDefinitionException if such bean does not exist
* @see CacheOperation#keyGenerator
* @see CacheOperation#cacheManager
* @see CacheOperation#cacheResolver
*/
protected T getBean(String beanName, Class expectedType) {
return BeanFactoryAnnotationUtils.qualifiedBeanOfType(this.beanFactory, expectedType, beanName);
}
/**
* Clear the cached metadata.
*/
protected void clearMetadataCache() {
this.metadataCache.clear();
this.evaluator.clear();
}
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 operations = getCacheOperationSource().getCacheOperations(method, targetClass);
if (!CollectionUtils.isEmpty(operations)) {
return execute(invoker, method, new CacheOperationContexts(operations, method, args, target, targetClass));
}
}
return invoker.invoke();
}
/**
* Execute the underlying operation (typically in case of cache miss) and return
* the result of the invocation. If an exception occurs it will be wrapped in
* a {@link CacheOperationInvoker.ThrowableWrapper}: the exception can be handled
* or modified but it must be wrapped in a
* {@link CacheOperationInvoker.ThrowableWrapper} as well.
* @param invoker the invoker handling the operation being cached
* @return the result of the invocation
* @see CacheOperationInvoker#invoke()
*/
protected Object invokeOperation(CacheOperationInvoker invoker) {
return invoker.invoke();
}
private Class getTargetClass(Object target) {
Class targetClass = AopProxyUtils.ultimateTargetClass(target);
if (targetClass == null && target != null) {
targetClass = target.getClass();
}
return targetClass;
}
private Object execute(final CacheOperationInvoker invoker, Method method, CacheOperationContexts contexts) {
// Special handling of synchronized invocation
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, new Callable