+ add XML support for cache abstraction (cache-advice) - DRAFT
This commit is contained in:
parent
a8476d05be
commit
63a217a40a
|
|
@ -48,11 +48,6 @@ import static org.springframework.context.annotation.AnnotationConfigUtils.*;
|
|||
*/
|
||||
class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser {
|
||||
|
||||
private static final String CACHE_MANAGER_ATTRIBUTE = "cache-manager";
|
||||
|
||||
private static final String DEFAULT_CACHE_MANAGER_BEAN_NAME = "cacheManager";
|
||||
|
||||
|
||||
/**
|
||||
* Parses the '<code><cache:annotation-driven/></code>' tag. Will
|
||||
* {@link AopNamespaceUtils#registerAutoProxyCreatorIfNecessary register an AutoProxyCreator}
|
||||
|
|
@ -71,13 +66,9 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
|
|||
return null;
|
||||
}
|
||||
|
||||
private static String extractCacheManager(Element element) {
|
||||
return (element.hasAttribute(CACHE_MANAGER_ATTRIBUTE) ? element.getAttribute(CACHE_MANAGER_ATTRIBUTE)
|
||||
: DEFAULT_CACHE_MANAGER_BEAN_NAME);
|
||||
}
|
||||
|
||||
private static void registerCacheManagerProperty(Element element, BeanDefinition def) {
|
||||
def.getPropertyValues().add("cacheManager", new RuntimeBeanReference(extractCacheManager(element)));
|
||||
def.getPropertyValues().add("cacheManager",
|
||||
new RuntimeBeanReference(CacheNamespaceHandler.extractCacheManager(element)));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -124,7 +115,7 @@ class AnnotationDrivenCacheBeanDefinitionParser implements BeanDefinitionParser
|
|||
interceptorDef.setSource(eleSource);
|
||||
interceptorDef.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
|
||||
registerCacheManagerProperty(element, interceptorDef);
|
||||
interceptorDef.getPropertyValues().add("cacheOperationSource", new RuntimeBeanReference(sourceName));
|
||||
interceptorDef.getPropertyValues().add("cacheOperationSources", new RuntimeBeanReference(sourceName));
|
||||
String interceptorName = parserContext.getReaderContext().registerWithGeneratedName(interceptorDef);
|
||||
|
||||
// Create the CacheAdvisor definition.
|
||||
|
|
|
|||
171
org.springframework.context/src/main/java/org/springframework/cache/config/CacheAdviceParser.java
vendored
Normal file
171
org.springframework.context/src/main/java/org/springframework/cache/config/CacheAdviceParser.java
vendored
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
/*
|
||||
* Copyright 2011 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.config;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
import org.springframework.beans.factory.parsing.ReaderContext;
|
||||
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
|
||||
import org.springframework.beans.factory.support.ManagedList;
|
||||
import org.springframework.beans.factory.support.ManagedMap;
|
||||
import org.springframework.beans.factory.support.RootBeanDefinition;
|
||||
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
|
||||
import org.springframework.beans.factory.xml.ParserContext;
|
||||
import org.springframework.cache.annotation.AnnotationCacheOperationSource;
|
||||
import org.springframework.cache.interceptor.CacheEvictOperation;
|
||||
import org.springframework.cache.interceptor.CacheInterceptor;
|
||||
import org.springframework.cache.interceptor.CacheOperation;
|
||||
import org.springframework.cache.interceptor.CacheUpdateOperation;
|
||||
import org.springframework.cache.interceptor.NameMatchCacheOperationSource;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.util.xml.DomUtils;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser
|
||||
* BeanDefinitionParser} for the {@code <tx:advice/>} tag.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*
|
||||
*/
|
||||
class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
/**
|
||||
* Simple, reusable class used for overriding defaults.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
private static class Props {
|
||||
|
||||
private String key, condition;
|
||||
private String[] caches = null;
|
||||
|
||||
Props(Element root) {
|
||||
String defaultCache = root.getAttribute("cache");
|
||||
key = root.getAttribute("key");
|
||||
condition = root.getAttribute("condition");
|
||||
|
||||
if (StringUtils.hasText(defaultCache)) {
|
||||
caches = StringUtils.commaDelimitedListToStringArray(defaultCache.trim());
|
||||
}
|
||||
}
|
||||
|
||||
CacheOperation merge(Element element, ReaderContext readerCtx, CacheOperation op) {
|
||||
String cache = element.getAttribute("cache");
|
||||
String k = element.getAttribute("key");
|
||||
String c = element.getAttribute("condition");
|
||||
|
||||
String[] localCaches = caches;
|
||||
String localKey = key, localCondition = condition;
|
||||
|
||||
// sanity check
|
||||
if (StringUtils.hasText(cache)) {
|
||||
localCaches = StringUtils.commaDelimitedListToStringArray(cache.trim());
|
||||
} else {
|
||||
if (caches == null) {
|
||||
readerCtx.error("No cache specified specified for " + element.getNodeName(), element);
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(k)) {
|
||||
localKey = k.trim();
|
||||
}
|
||||
|
||||
if (StringUtils.hasText(c)) {
|
||||
localCondition = c.trim();
|
||||
}
|
||||
op.setCacheNames(localCaches);
|
||||
op.setKey(localKey);
|
||||
op.setCondition(localCondition);
|
||||
|
||||
return op;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String CACHEABLE_ELEMENT = "cacheable";
|
||||
private static final String CACHE_EVICT_ELEMENT = "cache-evict";
|
||||
private static final String METHOD_ATTRIBUTE = "method";
|
||||
private static final String DEFS_ELEMENT = "definitions";
|
||||
|
||||
@Override
|
||||
protected Class<?> getBeanClass(Element element) {
|
||||
return CacheInterceptor.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
|
||||
builder.addPropertyReference("cacheManager", CacheNamespaceHandler.extractCacheManager(element));
|
||||
|
||||
List<Element> cacheDefs = DomUtils.getChildElementsByTagName(element, DEFS_ELEMENT);
|
||||
if (cacheDefs.size() >= 1) {
|
||||
// Using attributes source.
|
||||
List<RootBeanDefinition> attributeSourceDefinitions = parseDefinitionsSources(cacheDefs, parserContext);
|
||||
builder.addPropertyValue("cacheOperationSources", attributeSourceDefinitions);
|
||||
} else {
|
||||
// Assume annotations source.
|
||||
builder.addPropertyValue("cacheOperationSources", new RootBeanDefinition(
|
||||
AnnotationCacheOperationSource.class));
|
||||
}
|
||||
}
|
||||
|
||||
private List<RootBeanDefinition> parseDefinitionsSources(List<Element> definitions, ParserContext parserContext) {
|
||||
ManagedList<RootBeanDefinition> defs = new ManagedList<RootBeanDefinition>(definitions.size());
|
||||
|
||||
// extract default param for the definition
|
||||
for (Element element : definitions) {
|
||||
defs.add(parseDefinitionSource(element, parserContext));
|
||||
}
|
||||
|
||||
return defs;
|
||||
}
|
||||
|
||||
private RootBeanDefinition parseDefinitionSource(Element definition, ParserContext parserContext) {
|
||||
Props prop = new Props(definition);
|
||||
// add cacheable first
|
||||
|
||||
ManagedMap<TypedStringValue, CacheOperation> cacheOpeMap = new ManagedMap<TypedStringValue, CacheOperation>();
|
||||
cacheOpeMap.setSource(parserContext.extractSource(definition));
|
||||
|
||||
List<Element> updateCacheMethods = DomUtils.getChildElementsByTagName(definition, CACHEABLE_ELEMENT);
|
||||
|
||||
for (Element opElement : updateCacheMethods) {
|
||||
String name = opElement.getAttribute(METHOD_ATTRIBUTE);
|
||||
TypedStringValue nameHolder = new TypedStringValue(name);
|
||||
nameHolder.setSource(parserContext.extractSource(opElement));
|
||||
CacheOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CacheUpdateOperation());
|
||||
|
||||
cacheOpeMap.put(nameHolder, op);
|
||||
}
|
||||
|
||||
List<Element> evictCacheMethods = DomUtils.getChildElementsByTagName(definition, CACHE_EVICT_ELEMENT);
|
||||
|
||||
for (Element opElement : evictCacheMethods) {
|
||||
String name = opElement.getAttribute(METHOD_ATTRIBUTE);
|
||||
TypedStringValue nameHolder = new TypedStringValue(name);
|
||||
nameHolder.setSource(parserContext.extractSource(opElement));
|
||||
CacheOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CacheEvictOperation());
|
||||
|
||||
cacheOpeMap.put(nameHolder, op);
|
||||
}
|
||||
|
||||
RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchCacheOperationSource.class);
|
||||
attributeSourceDefinition.setSource(parserContext.extractSource(definition));
|
||||
attributeSourceDefinition.getPropertyValues().add("nameMap", cacheOpeMap);
|
||||
return attributeSourceDefinition;
|
||||
}
|
||||
}
|
||||
|
|
@ -17,6 +17,7 @@
|
|||
package org.springframework.cache.config;
|
||||
|
||||
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
/**
|
||||
* <code>NamespaceHandler</code> allowing for the configuration of
|
||||
|
|
@ -30,8 +31,18 @@ import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
|
|||
*/
|
||||
public class CacheNamespaceHandler extends NamespaceHandlerSupport {
|
||||
|
||||
static final String CACHE_MANAGER_ATTRIBUTE = "cache-manager";
|
||||
static final String DEFAULT_CACHE_MANAGER_BEAN_NAME = "cacheManager";
|
||||
|
||||
static String extractCacheManager(Element element) {
|
||||
return (element.hasAttribute(CacheNamespaceHandler.CACHE_MANAGER_ATTRIBUTE) ? element
|
||||
.getAttribute(CacheNamespaceHandler.CACHE_MANAGER_ATTRIBUTE)
|
||||
: CacheNamespaceHandler.DEFAULT_CACHE_MANAGER_BEAN_NAME);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
registerBeanDefinitionParser("annotation-driven", new AnnotationDrivenCacheBeanDefinitionParser());
|
||||
registerBeanDefinitionParser("advice", new CacheAdviceParser());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -117,11 +117,6 @@ public abstract class AbstractFallbackCacheOperationSource implements CacheOpera
|
|||
return new DefaultCacheKey(method, targetClass);
|
||||
}
|
||||
|
||||
/**
|
||||
* Same signature as {@link #getTransactionAttribute}, but doesn't cache the result.
|
||||
* {@link #getTransactionAttribute} is effectively a caching decorator for this method.
|
||||
* @see #getTransactionAttribute
|
||||
*/
|
||||
private CacheOperation computeCacheOperationDefinition(Method method, Class<?> targetClass) {
|
||||
// Don't allow no-public methods as required.
|
||||
if (allowPublicMethodsOnly() && !Modifier.isPublic(method.getModifiers())) {
|
||||
|
|
|
|||
|
|
@ -44,12 +44,8 @@ import org.springframework.util.StringUtils;
|
|||
*
|
||||
* <p>Subclasses are responsible for calling methods in this class in the correct order.
|
||||
*
|
||||
* <p>If no caching name has been specified in the <code>CacheOperationDefinition</code>,
|
||||
* the exposed name will be the <code>fully-qualified class name + "." + method name</code>
|
||||
* (by default).
|
||||
*
|
||||
* <p>Uses the <b>Strategy</b> design pattern. A <code>CacheManager</code>
|
||||
* implementation will perform the actual transaction management, and a
|
||||
* implementation will perform the actual cache management, and a
|
||||
* <code>CacheDefinitionSource</code> is used for determining caching operation definitions.
|
||||
*
|
||||
* <p>A cache aspect is serializable if its <code>CacheManager</code>
|
||||
|
|
@ -155,6 +151,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
return ClassUtils.getQualifiedMethodName(specificMethod);
|
||||
}
|
||||
|
||||
|
||||
protected Collection<Cache> getCaches(CacheOperation operation) {
|
||||
Set<String> cacheNames = operation.getCacheNames();
|
||||
Collection<Cache> caches = new ArrayList<Cache>(cacheNames.size());
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright 2011 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.beans.PropertyEditorSupport;
|
||||
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* PropertyEditor for {@link CacheOperation} objects. Accepts a String of form
|
||||
* <p><tt>action,cache,key,condition</tt>
|
||||
* <p>where only action and cache are required. Available definitions for action are
|
||||
* <tt>cacheable</tt> and <tt>evict</tt>.
|
||||
* When specifying multiple caches, use ; as a separator
|
||||
*
|
||||
* A typical example would be:
|
||||
* <p><code>cacheable, orders;books, #p0</code>
|
||||
*
|
||||
* <p>The tokens need to be specified in the order above.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*
|
||||
* @see org.springframework.transaction.TransactionAttributeEditor
|
||||
* @see org.springframework.core.Constants
|
||||
*/
|
||||
public class CacheOperationEditor extends PropertyEditorSupport {
|
||||
|
||||
/**
|
||||
* Format is action, cache, key, condition.
|
||||
* Null or the empty string means that the method is non cacheable.
|
||||
* @see java.beans.PropertyEditor#setAsText(java.lang.String)
|
||||
*/
|
||||
@Override
|
||||
public void setAsText(String text) throws IllegalArgumentException {
|
||||
if (StringUtils.hasLength(text)) {
|
||||
// tokenize it with ","
|
||||
String[] tokens = StringUtils.commaDelimitedListToStringArray(text);
|
||||
if (tokens.length < 2) {
|
||||
throw new IllegalArgumentException(
|
||||
"too little arguments found, at least the cache action and cache name are required");
|
||||
}
|
||||
|
||||
CacheOperation op;
|
||||
|
||||
if ("cacheable".contains(tokens[0])) {
|
||||
op = new CacheUpdateOperation();
|
||||
}
|
||||
|
||||
else if ("evict".contains(tokens[0])) {
|
||||
op = new CacheEvictOperation();
|
||||
} else {
|
||||
throw new IllegalArgumentException("Invalid cache action specified " + tokens[0]);
|
||||
}
|
||||
|
||||
op.setCacheNames(StringUtils.delimitedListToStringArray(tokens[1], ";"));
|
||||
|
||||
if (tokens.length > 2) {
|
||||
op.setKey(tokens[2]);
|
||||
}
|
||||
|
||||
if (tokens.length > 3) {
|
||||
op.setCondition(tokens[3]);
|
||||
}
|
||||
|
||||
setValue(op);
|
||||
} else {
|
||||
setValue(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/*
|
||||
* Copyright 2010-2011 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 org.springframework.aop.Pointcut;
|
||||
import org.springframework.aop.framework.AbstractSingletonProxyFactoryBean;
|
||||
|
||||
/**
|
||||
* Proxy factory bean for simplified declarative caching handling.
|
||||
* This is a convenient alternative to a standard AOP
|
||||
* {@link org.springframework.aop.framework.ProxyFactoryBean}
|
||||
* with a separate {@link CachingInterceptor} definition.
|
||||
*
|
||||
* <p>This class is intended to cover the <i>typical</i> case of declarative
|
||||
* cache demarcation: namely, wrapping a singleton target object with a
|
||||
* caching proxy, proxying all the interfaces that the target implements.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @see org.springframework.aop.framework.ProxyFactoryBean
|
||||
* @see CachingInterceptor
|
||||
*/
|
||||
public class CacheProxyFactoryBean extends AbstractSingletonProxyFactoryBean {
|
||||
|
||||
private final CacheInterceptor cachingInterceptor = new CacheInterceptor();
|
||||
private Pointcut pointcut;
|
||||
|
||||
@Override
|
||||
protected Object createMainInterceptor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the caching attribute source which is used to find the cache operation
|
||||
* definition.
|
||||
*
|
||||
* @param cacheDefinitionSources cache definition sources
|
||||
*/
|
||||
public void setCacheDefinitionSources(CacheOperationSource... cacheDefinitionSources) {
|
||||
this.cachingInterceptor.setCacheOperationSources(cacheDefinitionSources);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,148 @@
|
|||
/*
|
||||
* Copyright 2011 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.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Enumeration;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
import org.springframework.util.PatternMatchUtils;
|
||||
|
||||
/**
|
||||
* Simple {@link CacheOperationSource} implementation that
|
||||
* allows attributes to be matched by registered name.
|
||||
*
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class NameMatchCacheOperationSource implements CacheOperationSource, Serializable {
|
||||
|
||||
/**
|
||||
* Logger available to subclasses.
|
||||
* <p>Static for optimal serialization.
|
||||
*/
|
||||
protected static final Log logger = LogFactory.getLog(NameMatchCacheOperationSource.class);
|
||||
|
||||
/** Keys are method names; values are TransactionAttributes */
|
||||
private Map<String, CacheOperation> nameMap = new LinkedHashMap<String, CacheOperation>();
|
||||
|
||||
/**
|
||||
* Set a name/attribute map, consisting of method names
|
||||
* (e.g. "myMethod") and CacheOperation instances
|
||||
* (or Strings to be converted to CacheOperation instances).
|
||||
* @see CacheOperation
|
||||
* @see CacheOperationEditor
|
||||
*/
|
||||
public void setNameMap(Map<String, CacheOperation> nameMap) {
|
||||
for (Map.Entry<String, CacheOperation> entry : nameMap.entrySet()) {
|
||||
addCacheMethod(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the given properties into a name/attribute map.
|
||||
* Expects method names as keys and String attributes definitions as values,
|
||||
* parsable into CacheOperation instances via CacheOperationEditor.
|
||||
* @see #setNameMap
|
||||
* @see CacheOperationEditor
|
||||
*/
|
||||
public void setProperties(Properties cacheOperations) {
|
||||
CacheOperationEditor tae = new CacheOperationEditor();
|
||||
Enumeration propNames = cacheOperations.propertyNames();
|
||||
while (propNames.hasMoreElements()) {
|
||||
String methodName = (String) propNames.nextElement();
|
||||
String value = cacheOperations.getProperty(methodName);
|
||||
tae.setAsText(value);
|
||||
CacheOperation op = (CacheOperation) tae.getValue();
|
||||
addCacheMethod(methodName, op);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an attribute for a cacheable method.
|
||||
* <p>Method names can be exact matches, or of the pattern "xxx*",
|
||||
* "*xxx" or "*xxx*" for matching multiple methods.
|
||||
* @param methodName the name of the method
|
||||
* @param operation operation associated with the method
|
||||
*/
|
||||
public void addCacheMethod(String methodName, CacheOperation operation) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Adding method [" + methodName + "] with cache operation [" + operation + "]");
|
||||
}
|
||||
this.nameMap.put(methodName, operation);
|
||||
}
|
||||
|
||||
public CacheOperation getCacheOperation(Method method, Class<?> targetClass) {
|
||||
// look for direct name match
|
||||
String methodName = method.getName();
|
||||
CacheOperation attr = this.nameMap.get(methodName);
|
||||
|
||||
if (attr == null) {
|
||||
// Look for most specific name match.
|
||||
String bestNameMatch = null;
|
||||
for (String mappedName : this.nameMap.keySet()) {
|
||||
if (isMatch(methodName, mappedName)
|
||||
&& (bestNameMatch == null || bestNameMatch.length() <= mappedName.length())) {
|
||||
attr = this.nameMap.get(mappedName);
|
||||
bestNameMatch = mappedName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return attr;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return if the given method name matches the mapped name.
|
||||
* <p>The default implementation checks for "xxx*", "*xxx" and "*xxx*" matches,
|
||||
* as well as direct equality. Can be overridden in subclasses.
|
||||
* @param methodName the method name of the class
|
||||
* @param mappedName the name in the descriptor
|
||||
* @return if the names match
|
||||
* @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String)
|
||||
*/
|
||||
protected boolean isMatch(String methodName, String mappedName) {
|
||||
return PatternMatchUtils.simpleMatch(mappedName, methodName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object other) {
|
||||
if (this == other) {
|
||||
return true;
|
||||
}
|
||||
if (!(other instanceof NameMatchCacheOperationSource)) {
|
||||
return false;
|
||||
}
|
||||
NameMatchCacheOperationSource otherTas = (NameMatchCacheOperationSource) other;
|
||||
return ObjectUtils.nullSafeEquals(this.nameMap, otherTas.nameMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return NameMatchCacheOperationSource.class.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getName() + ": " + this.nameMap;
|
||||
}
|
||||
}
|
||||
|
|
@ -89,4 +89,112 @@
|
|||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:element name="advice">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.cache.interceptor.CacheInterceptor"><![CDATA[
|
||||
Defines the cache semantics of the AOP advice that is to be
|
||||
executed.
|
||||
|
||||
That is, this advice element is where the cacheable semantics of
|
||||
any number of methods are defined (where cacheable semantics
|
||||
includes the backing cache(s), the key, cache condition rules, and suchlike).
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation>
|
||||
<tool:exports type="java:org.springframework.cache.interceptor.CacheInterceptor"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="beans:identifiedType">
|
||||
<xsd:sequence>
|
||||
<xsd:element name="definitions" type="definitionsType" minOccurs="0" maxOccurs="unbounded"/>
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="cache-manager" type="xsd:string" default="cacheManager">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.cache.CacheManager"><![CDATA[
|
||||
The bean name of the CacheManager that is to be used
|
||||
for storing and retrieving data.
|
||||
|
||||
This attribute is not required, and only needs to be specified
|
||||
explicitly if the bean name of the desired CacheManager
|
||||
is not 'cacheManager'.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.cache.CacheManager"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
<xsd:complexType name="basedefinitionType">
|
||||
<xsd:attribute name="cache" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the backing cache(s). Multiple caches can be specified by separating them using comma: 'orders, books']]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="key" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The SpEL expression used for computing the cache key.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="condition" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The SpEL expression used for conditioning the method caching.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="definitionType">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="basedefinitionType">
|
||||
<xsd:attribute name="method" type="xsd:string" use="required">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The method name(s) with which the cache attributes are to be
|
||||
associated. The wildcard (*) character can be used to associate the
|
||||
same cache attribute settings with a number of methods; for
|
||||
example, 'get*', 'handle*', '*Order', 'on*Event', etc.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="definitionsType">
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="basedefinitionType">
|
||||
<xsd:sequence>
|
||||
<xsd:choice>
|
||||
<xsd:element name="cacheable" minOccurs="0" maxOccurs="unbounded" type="definitionType"/>
|
||||
<xsd:element name="cache-evict" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="definitionType">
|
||||
<xsd:attribute name="all-entries" type="xsd:boolean" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The name of the backing cache.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:sequence>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
||||
</xsd:schema>
|
||||
|
|
@ -86,7 +86,7 @@ public abstract class AbstractAnnotationTests {
|
|||
Object r2 = service.cache(o1);
|
||||
|
||||
assertSame(r1, r2);
|
||||
service.invalidate(o1, null);
|
||||
service.evict(o1, null);
|
||||
Object r3 = service.cache(o1);
|
||||
Object r4 = service.cache(o1);
|
||||
assertNotSame(r1, r3);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public class AnnotatedClassCacheableService implements CacheableService {
|
|||
}
|
||||
|
||||
@CacheEvict(value = "default", key = "#p0")
|
||||
public void invalidate(Object arg1, Object arg2) {
|
||||
public void evict(Object arg1, Object arg2) {
|
||||
}
|
||||
|
||||
@Cacheable(value = "default", key = "#p0")
|
||||
|
|
|
|||
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2010-2011 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.config;
|
||||
|
||||
|
||||
/**
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public class CacheAdviceNamespaceTests extends AbstractAnnotationTests {
|
||||
|
||||
|
||||
@Override
|
||||
protected String getConfig() {
|
||||
return "/org/springframework/cache/config/cache-advice.xml";
|
||||
}
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ public interface CacheableService<T> {
|
|||
|
||||
void invalidate(Object arg1);
|
||||
|
||||
void invalidate(Object arg1, Object arg2);
|
||||
void evict(Object arg1, Object arg2);
|
||||
|
||||
T conditional(int field);
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ public class DefaultCacheableService implements CacheableService<Long> {
|
|||
}
|
||||
|
||||
@CacheEvict(value = "default", key = "#p0")
|
||||
public void invalidate(Object arg1, Object arg2) {
|
||||
public void evict(Object arg1, Object arg2) {
|
||||
}
|
||||
|
||||
@Cacheable(value = "default", condition = "#classField == 3")
|
||||
|
|
|
|||
59
org.springframework.context/src/test/resources/org/springframework/cache/config/cache-advice.xml
vendored
Normal file
59
org.springframework.context/src/test/resources/org/springframework/cache/config/cache-advice.xml
vendored
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns:aop="http://www.springframework.org/schema/aop"
|
||||
xmlns:cache="http://www.springframework.org/schema/cache"
|
||||
xmlns:p="http://www.springframework.org/schema/p"
|
||||
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
|
||||
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
|
||||
http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd">
|
||||
|
||||
<cache:advice id="cacheAdviceInterface" cache-manager="cacheManager">
|
||||
<cache:definitions cache="default">
|
||||
<cache:cacheable method="cache"/>
|
||||
<cache:cacheable method="conditional" condition="#classField == 3"/>
|
||||
<cache:cacheable method="key" key="#p0"/>
|
||||
<cache:cacheable method="nam*" key="#root.methodName"/>
|
||||
<cache:cacheable method="rootVars" key="#root.methodName + #root.method.name + #root.targetClass + #root.target"/>
|
||||
<cache:cacheable method="nullValue" cache="default"/>
|
||||
</cache:definitions>
|
||||
<cache:definitions>
|
||||
<cache:cache-evict method="invalidate" cache="default"/>
|
||||
<cache:cache-evict method="evict" key="#p0" cache="default"/>
|
||||
</cache:definitions>
|
||||
</cache:advice>
|
||||
|
||||
<cache:advice id="cacheAdviceClass" cache-manager="cacheManager">
|
||||
<cache:definitions cache="default">
|
||||
<cache:cacheable method="key" key="#p0"/>
|
||||
<cache:cacheable method="nam*" key="#root.methodName + #root.caches[0].name"/>
|
||||
<cache:cacheable method="rootVars" key="#root.methodName + #root.method.name + #root.targetClass + #root.target"/>
|
||||
<cache:cacheable method="cache"/>
|
||||
<cache:cacheable method="conditional"/>
|
||||
<cache:cacheable method="null*"/>
|
||||
</cache:definitions>
|
||||
<cache:definitions>
|
||||
<cache:cache-evict method="invalidate" cache="default"/>
|
||||
<cache:cache-evict method="evict" key="#p0" cache="default"/>
|
||||
</cache:definitions>
|
||||
</cache:advice>
|
||||
|
||||
<aop:config>
|
||||
<aop:advisor advice-ref="cacheAdviceInterface" pointcut="execution(* *..DefaultCacheableService.*(..))" order="1"/>
|
||||
<aop:advisor advice-ref="cacheAdviceClass" pointcut="execution(* *..AnnotatedClassCacheableService.*(..))" order="1"/>
|
||||
<aop:advisor advice-ref="debugInterceptor" pointcut="execution(* *..CacheableService.*(..))" order="2"/>
|
||||
</aop:config>
|
||||
|
||||
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
|
||||
<property name="caches">
|
||||
<set>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
|
||||
</set>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
<bean id="debugInterceptor" class="org.springframework.aop.interceptor.DebugInterceptor"/>
|
||||
|
||||
<bean id="service" class="org.springframework.cache.config.DefaultCacheableService"/>
|
||||
|
||||
<bean id="classService" class="org.springframework.cache.config.AnnotatedClassCacheableService"/>
|
||||
</beans>
|
||||
Loading…
Reference in New Issue