+ add XML support for cache abstraction (cache-advice) - DRAFT

This commit is contained in:
Costin Leau 2011-08-16 17:35:01 +00:00
parent a8476d05be
commit 63a217a40a
15 changed files with 676 additions and 26 deletions

View File

@ -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>&lt;cache:annotation-driven/&gt;</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.

View 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;
}
}

View File

@ -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());
}
}

View File

@ -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())) {

View File

@ -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());

View File

@ -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);
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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>

View File

@ -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);

View File

@ -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")

View File

@ -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";
}
}

View File

@ -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);

View File

@ -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")

View 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>