SPR-8830
SPR-8082 SPR-7833 + add support for CacheDefinitions declarations inside XML + more integration tests
This commit is contained in:
parent
e4c88553d8
commit
dc88a7c8ba
|
|
@ -0,0 +1,107 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
|
||||
<xsd:schema xmlns="http://www.springframework.org/schema/cache"
|
||||
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
|
||||
xmlns:beans="http://www.springframework.org/schema/beans"
|
||||
xmlns:tool="http://www.springframework.org/schema/tool"
|
||||
targetNamespace="http://www.springframework.org/schema/cache"
|
||||
elementFormDefault="qualified"
|
||||
attributeFormDefault="unqualified">
|
||||
|
||||
<xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"/>
|
||||
<xsd:import namespace="http://www.springframework.org/schema/tool" schemaLocation="http://www.springframework.org/schema/tool/spring-tool-3.0.xsd"/>
|
||||
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Defines the elements used in the Spring Framework's declarative
|
||||
cache management infrastructure.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
|
||||
<xsd:element name="annotation-driven">
|
||||
<xsd:complexType>
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.cache.annotation.AnnotationCacheOperationDefinitionSource"><![CDATA[
|
||||
Indicates that cache configuration is defined by Java 5
|
||||
annotations on bean classes, and that proxies are automatically
|
||||
to be created for the relevant annotated beans.
|
||||
|
||||
The default annotations supported are Spring's @Cacheable and @CacheEvict.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<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 to retrieve the backing caches.
|
||||
|
||||
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:attribute name="key-generator" type="xsd:string">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.cache.interceptor.KeyGenerator"><![CDATA[
|
||||
The bean name of the KeyGenerator that is to be used to retrieve the backing caches.
|
||||
|
||||
This attribute is not required, and only needs to be specified
|
||||
explicitly if the default strategy (DefaultKeyGenerator) is not sufficient.
|
||||
]]></xsd:documentation>
|
||||
<xsd:appinfo>
|
||||
<tool:annotation kind="ref">
|
||||
<tool:expected-type type="org.springframework.cache.interceptor.KeyGenerator"/>
|
||||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="mode" default="proxy">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Should annotated beans be proxied using Spring's AOP framework,
|
||||
or should they rather be weaved with an AspectJ transaction aspect?
|
||||
|
||||
AspectJ weaving requires spring-aspects.jar on the classpath,
|
||||
as well as load-time weaving (or compile-time weaving) enabled.
|
||||
|
||||
Note: The weaving-based aspect requires the @Cacheable and @CacheInvalidate
|
||||
annotations to be defined on the concrete class. Annotations in interfaces
|
||||
will not work in that case (they will rather only work with interface-based proxies)!
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
<xsd:simpleType>
|
||||
<xsd:restriction base="xsd:string">
|
||||
<xsd:enumeration value="proxy"/>
|
||||
<xsd:enumeration value="aspectj"/>
|
||||
</xsd:restriction>
|
||||
</xsd:simpleType>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="proxy-target-class" type="xsd:boolean" default="false">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Are class-based (CGLIB) proxies to be created? By default, standard
|
||||
Java interface-based proxies are created.
|
||||
|
||||
Note: Class-based proxies require the @Cacheable and @CacheInvalidate annotations
|
||||
to be defined on the concrete class. Annotations in interfaces will not work
|
||||
in that case (they will rather only work with interface-based proxies)!
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
<xsd:attribute name="order" type="xsd:int">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation source="java:org.springframework.core.Ordered"><![CDATA[
|
||||
Controls the ordering of the execution of the cache advisor
|
||||
when multiple advice executes at a specific joinpoint.
|
||||
]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
|
||||
</xsd:schema>
|
||||
|
|
@ -34,7 +34,7 @@ import org.springframework.util.Assert;
|
|||
* Implementation of the {@link org.springframework.cache.interceptor.CacheOperationSource}
|
||||
* interface for working with caching metadata in JDK 1.5+ annotation format.
|
||||
*
|
||||
* <p>This class reads Spring's JDK 1.5+ {@link Cacheable}, {@link CacheUpdate} and {@link CacheEvict}
|
||||
* <p>This class reads Spring's JDK 1.5+ {@link Cacheable}, {@link CachePut} and {@link CacheEvict}
|
||||
* annotations and exposes corresponding caching operation definition to Spring's cache infrastructure.
|
||||
* This class may also serve as base class for a custom CacheOperationSource.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ import org.springframework.cache.interceptor.CacheOperation;
|
|||
* Strategy interface for parsing known caching annotation types.
|
||||
* {@link AnnotationCacheDefinitionSource} delegates to such
|
||||
* parsers for supporting specific annotation types such as Spring's own
|
||||
* {@link Cacheable}, {@link CacheUpdate} or {@link CacheEvict}.
|
||||
* {@link Cacheable}, {@link CachePut} or {@link CacheEvict}.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ import java.lang.annotation.RetentionPolicy;
|
|||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Group annotation for multiple cacheable annotations (of different or the same type).
|
||||
* Group annotation for multiple cache annotations (of different or the same type).
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
|
|
@ -33,11 +33,11 @@ import java.lang.annotation.Target;
|
|||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface CacheDefinition {
|
||||
public @interface CacheDefinitions {
|
||||
|
||||
Cacheable[] cacheables();
|
||||
Cacheable[] cacheable() default {};
|
||||
|
||||
CacheUpdate[] updates();
|
||||
CachePut[] put() default {};
|
||||
|
||||
CacheEvict[] evicts();
|
||||
CacheEvict[] evict() default {};
|
||||
}
|
||||
|
|
@ -23,11 +23,13 @@ import java.lang.annotation.Retention;
|
|||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
import org.springframework.cache.Cache;
|
||||
|
||||
/**
|
||||
*
|
||||
* Annotation indicating that a method (or all methods on a class) trigger(s)
|
||||
* a cache update operation. As opposed to {@link Cacheable} annotation, this annotation
|
||||
* does not cause the target method to be skipped in case of a cache hit - rather it
|
||||
* a {@link Cache#put(Object, Object)} operation. As opposed to {@link Cacheable} annotation,
|
||||
* this annotation does not cause the target method to be skipped - rather it
|
||||
* always causes the method to be invoked and its result to be placed into the cache.
|
||||
*
|
||||
* @author Costin Leau
|
||||
|
|
@ -37,7 +39,7 @@ import java.lang.annotation.Target;
|
|||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface CacheUpdate {
|
||||
public @interface CachePut {
|
||||
|
||||
/**
|
||||
* Name of the caches in which the update takes place.
|
||||
|
|
@ -23,13 +23,13 @@ import java.util.Collection;
|
|||
|
||||
import org.springframework.cache.interceptor.CacheEvictOperation;
|
||||
import org.springframework.cache.interceptor.CacheOperation;
|
||||
import org.springframework.cache.interceptor.CacheUpdateOperation;
|
||||
import org.springframework.cache.interceptor.CachePutOperation;
|
||||
import org.springframework.cache.interceptor.CacheableOperation;
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* Strategy implementation for parsing Spring's {@link Cacheable}, {@link CacheEvict} and {@link CacheUpdate} annotations.
|
||||
* Strategy implementation for parsing Spring's {@link Cacheable}, {@link CacheEvict} and {@link CachePut} annotations.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
|
|
@ -51,12 +51,12 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
|||
ops = lazyInit(ops);
|
||||
ops.add(parseEvictAnnotation(ae, evict));
|
||||
}
|
||||
CacheUpdate update = AnnotationUtils.getAnnotation(ae, CacheUpdate.class);
|
||||
CachePut update = AnnotationUtils.getAnnotation(ae, CachePut.class);
|
||||
if (update != null) {
|
||||
ops = lazyInit(ops);
|
||||
ops.add(parseUpdateAnnotation(ae, update));
|
||||
}
|
||||
CacheDefinition definition = AnnotationUtils.getAnnotation(ae, CacheDefinition.class);
|
||||
CacheDefinitions definition = AnnotationUtils.getAnnotation(ae, CacheDefinitions.class);
|
||||
if (definition != null) {
|
||||
ops = lazyInit(ops);
|
||||
ops.addAll(parseDefinitionAnnotation(ae, definition));
|
||||
|
|
@ -87,8 +87,8 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
|||
return ceo;
|
||||
}
|
||||
|
||||
CacheOperation parseUpdateAnnotation(AnnotatedElement ae, CacheUpdate ann) {
|
||||
CacheUpdateOperation cuo = new CacheUpdateOperation();
|
||||
CacheOperation parseUpdateAnnotation(AnnotatedElement ae, CachePut ann) {
|
||||
CachePutOperation cuo = new CachePutOperation();
|
||||
cuo.setCacheNames(ann.value());
|
||||
cuo.setCondition(ann.condition());
|
||||
cuo.setKey(ann.key());
|
||||
|
|
@ -96,27 +96,27 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
|||
return cuo;
|
||||
}
|
||||
|
||||
Collection<CacheOperation> parseDefinitionAnnotation(AnnotatedElement ae, CacheDefinition ann) {
|
||||
Collection<CacheOperation> parseDefinitionAnnotation(AnnotatedElement ae, CacheDefinitions ann) {
|
||||
Collection<CacheOperation> ops = null;
|
||||
|
||||
Cacheable[] cacheables = ann.cacheables();
|
||||
Cacheable[] cacheables = ann.cacheable();
|
||||
if (!ObjectUtils.isEmpty(cacheables)) {
|
||||
ops = lazyInit(ops);
|
||||
for (Cacheable cacheable : cacheables) {
|
||||
ops.add(parseCacheableAnnotation(ae, cacheable));
|
||||
}
|
||||
}
|
||||
CacheEvict[] evicts = ann.evicts();
|
||||
CacheEvict[] evicts = ann.evict();
|
||||
if (!ObjectUtils.isEmpty(evicts)) {
|
||||
ops = lazyInit(ops);
|
||||
for (CacheEvict evict : evicts) {
|
||||
ops.add(parseEvictAnnotation(ae, evict));
|
||||
}
|
||||
}
|
||||
CacheUpdate[] updates = ann.updates();
|
||||
CachePut[] updates = ann.put();
|
||||
if (!ObjectUtils.isEmpty(updates)) {
|
||||
ops = lazyInit(ops);
|
||||
for (CacheUpdate update : updates) {
|
||||
for (CachePut update : updates) {
|
||||
ops.add(parseUpdateAnnotation(ae, update));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.cache.config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import org.springframework.beans.factory.config.TypedStringValue;
|
||||
|
|
@ -30,6 +32,7 @@ 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.CachePutOperation;
|
||||
import org.springframework.cache.interceptor.CacheableOperation;
|
||||
import org.springframework.cache.interceptor.NameMatchCacheOperationSource;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
|
@ -52,13 +55,14 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
|
|||
*/
|
||||
private static class Props {
|
||||
|
||||
private String key, condition;
|
||||
private String key, condition, method;
|
||||
private String[] caches = null;
|
||||
|
||||
Props(Element root) {
|
||||
String defaultCache = root.getAttribute("cache");
|
||||
key = root.getAttribute("key");
|
||||
condition = root.getAttribute("condition");
|
||||
method = root.getAttribute(METHOD_ATTRIBUTE);
|
||||
|
||||
if (StringUtils.hasText(defaultCache)) {
|
||||
caches = StringUtils.commaDelimitedListToStringArray(defaultCache.trim());
|
||||
|
|
@ -95,10 +99,24 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
|
|||
|
||||
return op;
|
||||
}
|
||||
|
||||
String merge(Element element, ReaderContext readerCtx) {
|
||||
String m = element.getAttribute(METHOD_ATTRIBUTE);
|
||||
|
||||
if (StringUtils.hasText(m)) {
|
||||
return m.trim();
|
||||
}
|
||||
if (StringUtils.hasText(method)) {
|
||||
return method;
|
||||
}
|
||||
readerCtx.error("No method specified for " + element.getNodeName(), element);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String CACHEABLE_ELEMENT = "cacheable";
|
||||
private static final String CACHE_EVICT_ELEMENT = "cache-evict";
|
||||
private static final String CACHE_PUT_ELEMENT = "cache-put";
|
||||
private static final String METHOD_ATTRIBUTE = "method";
|
||||
private static final String DEFS_ELEMENT = "definitions";
|
||||
|
||||
|
|
@ -139,34 +157,60 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
|
|||
Props prop = new Props(definition);
|
||||
// add cacheable first
|
||||
|
||||
ManagedMap<TypedStringValue, CacheOperation> cacheOpeMap = new ManagedMap<TypedStringValue, CacheOperation>();
|
||||
cacheOpeMap.setSource(parserContext.extractSource(definition));
|
||||
ManagedMap<TypedStringValue, Collection<CacheOperation>> cacheOpMap = new ManagedMap<TypedStringValue, Collection<CacheOperation>>();
|
||||
cacheOpMap.setSource(parserContext.extractSource(definition));
|
||||
|
||||
List<Element> updateCacheMethods = DomUtils.getChildElementsByTagName(definition, CACHEABLE_ELEMENT);
|
||||
List<Element> cacheableCacheMethods = DomUtils.getChildElementsByTagName(definition, CACHEABLE_ELEMENT);
|
||||
|
||||
for (Element opElement : updateCacheMethods) {
|
||||
String name = opElement.getAttribute(METHOD_ATTRIBUTE);
|
||||
for (Element opElement : cacheableCacheMethods) {
|
||||
String name = prop.merge(opElement, parserContext.getReaderContext());
|
||||
TypedStringValue nameHolder = new TypedStringValue(name);
|
||||
nameHolder.setSource(parserContext.extractSource(opElement));
|
||||
CacheOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CacheableOperation());
|
||||
|
||||
cacheOpeMap.put(nameHolder, op);
|
||||
Collection<CacheOperation> col = cacheOpMap.get(nameHolder);
|
||||
if (col == null) {
|
||||
col = new ArrayList<CacheOperation>(2);
|
||||
cacheOpMap.put(nameHolder, col);
|
||||
}
|
||||
col.add(op);
|
||||
}
|
||||
|
||||
List<Element> evictCacheMethods = DomUtils.getChildElementsByTagName(definition, CACHE_EVICT_ELEMENT);
|
||||
|
||||
for (Element opElement : evictCacheMethods) {
|
||||
String name = opElement.getAttribute(METHOD_ATTRIBUTE);
|
||||
String name = prop.merge(opElement, parserContext.getReaderContext());
|
||||
TypedStringValue nameHolder = new TypedStringValue(name);
|
||||
nameHolder.setSource(parserContext.extractSource(opElement));
|
||||
CacheOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CacheEvictOperation());
|
||||
|
||||
cacheOpeMap.put(nameHolder, op);
|
||||
Collection<CacheOperation> col = cacheOpMap.get(nameHolder);
|
||||
if (col == null) {
|
||||
col = new ArrayList<CacheOperation>(2);
|
||||
cacheOpMap.put(nameHolder, col);
|
||||
}
|
||||
col.add(op);
|
||||
}
|
||||
|
||||
List<Element> putCacheMethods = DomUtils.getChildElementsByTagName(definition, CACHE_PUT_ELEMENT);
|
||||
|
||||
for (Element opElement : putCacheMethods) {
|
||||
String name = prop.merge(opElement, parserContext.getReaderContext());
|
||||
TypedStringValue nameHolder = new TypedStringValue(name);
|
||||
nameHolder.setSource(parserContext.extractSource(opElement));
|
||||
CacheOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CachePutOperation());
|
||||
|
||||
Collection<CacheOperation> col = cacheOpMap.get(nameHolder);
|
||||
if (col == null) {
|
||||
col = new ArrayList<CacheOperation>(2);
|
||||
cacheOpMap.put(nameHolder, col);
|
||||
}
|
||||
col.add(op);
|
||||
}
|
||||
|
||||
RootBeanDefinition attributeSourceDefinition = new RootBeanDefinition(NameMatchCacheOperationSource.class);
|
||||
attributeSourceDefinition.setSource(parserContext.extractSource(definition));
|
||||
attributeSourceDefinition.getPropertyValues().add("nameMap", cacheOpeMap);
|
||||
attributeSourceDefinition.getPropertyValues().add("nameMap", cacheOpMap);
|
||||
return attributeSourceDefinition;
|
||||
}
|
||||
}
|
||||
|
|
@ -403,7 +403,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
evicts.add(opContext);
|
||||
}
|
||||
|
||||
if (cacheOperation instanceof CacheUpdateOperation) {
|
||||
if (cacheOperation instanceof CachePutOperation) {
|
||||
updates.add(opContext);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,84 +0,0 @@
|
|||
/*
|
||||
* 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 CacheableOperation();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,11 +17,11 @@
|
|||
package org.springframework.cache.interceptor;
|
||||
|
||||
/**
|
||||
* Class describing a cache 'update' operation.
|
||||
* Class describing a cache 'put' operation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @since 3.1
|
||||
*/
|
||||
public class CacheUpdateOperation extends CacheOperation {
|
||||
public class CachePutOperation extends CacheOperation {
|
||||
|
||||
}
|
||||
|
|
@ -19,11 +19,8 @@ package org.springframework.cache.interceptor;
|
|||
import java.io.Serializable;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
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;
|
||||
|
|
@ -54,63 +51,44 @@ public class NameMatchCacheOperationSource implements CacheOperationSource, Seri
|
|||
* @see CacheOperation
|
||||
* @see CacheOperationEditor
|
||||
*/
|
||||
public void setNameMap(Map<String, CacheOperation> nameMap) {
|
||||
for (Map.Entry<String, CacheOperation> entry : nameMap.entrySet()) {
|
||||
public void setNameMap(Map<String, Collection<CacheOperation>> nameMap) {
|
||||
for (Map.Entry<String, Collection<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
|
||||
* @param ops operation associated with the method
|
||||
*/
|
||||
public void addCacheMethod(String methodName, CacheOperation operation) {
|
||||
public void addCacheMethod(String methodName, Collection<CacheOperation> ops) {
|
||||
if (logger.isDebugEnabled()) {
|
||||
logger.debug("Adding method [" + methodName + "] with cache operation [" + operation + "]");
|
||||
logger.debug("Adding method [" + methodName + "] with cache operations [" + ops + "]");
|
||||
}
|
||||
this.nameMap.put(methodName, Collections.singleton(operation));
|
||||
this.nameMap.put(methodName, ops);
|
||||
}
|
||||
|
||||
public Collection<CacheOperation> getCacheOperations(Method method, Class<?> targetClass) {
|
||||
// look for direct name match
|
||||
String methodName = method.getName();
|
||||
Collection<CacheOperation> attr = this.nameMap.get(methodName);
|
||||
Collection<CacheOperation> ops = this.nameMap.get(methodName);
|
||||
|
||||
if (attr == null) {
|
||||
if (ops == 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);
|
||||
ops = this.nameMap.get(mappedName);
|
||||
bestNameMatch = mappedName;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return attr;
|
||||
return ops;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -182,35 +182,29 @@
|
|||
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[
|
||||
<xsd:attribute name="method" type="xsd:string" use="optional">
|
||||
<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>
|
||||
example, 'get*', 'handle*', '*Order', 'on*Event', etc.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
|
||||
</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:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:element name="cacheable" minOccurs="0" maxOccurs="unbounded" type="basedefinitionType"/>
|
||||
<xsd:element name="cache-put" minOccurs="0" maxOccurs="unbounded" type="basedefinitionType"/>
|
||||
<xsd:element name="cache-evict" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="definitionType">
|
||||
<xsd:extension base="basedefinitionType">
|
||||
<xsd:attribute name="all-entries" type="xsd:boolean" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ package org.springframework.cache.config;
|
|||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Before;
|
||||
|
|
@ -51,6 +52,10 @@ public abstract class AbstractAnnotationTests {
|
|||
cs = ctx.getBean("service", CacheableService.class);
|
||||
ccs = ctx.getBean("classService", CacheableService.class);
|
||||
cm = ctx.getBean(CacheManager.class);
|
||||
Collection<String> cn = cm.getCacheNames();
|
||||
assertTrue(cn.contains("default"));
|
||||
assertTrue(cn.contains("secondary"));
|
||||
assertTrue(cn.contains("primary"));
|
||||
}
|
||||
|
||||
public void testCacheable(CacheableService service) throws Exception {
|
||||
|
|
@ -201,6 +206,124 @@ public abstract class AbstractAnnotationTests {
|
|||
assertEquals(three, Integer.valueOf(cache.get(three).get().toString()));
|
||||
}
|
||||
|
||||
public void testMultiCache(CacheableService service) {
|
||||
Object o1 = new Object();
|
||||
Object o2 = new Object();
|
||||
|
||||
Cache primary = cm.getCache("primary");
|
||||
Cache secondary = cm.getCache("secondary");
|
||||
|
||||
assertNull(primary.get(o1));
|
||||
assertNull(secondary.get(o1));
|
||||
Object r1 = service.multiCache(o1);
|
||||
assertSame(r1, primary.get(o1).get());
|
||||
assertSame(r1, secondary.get(o1).get());
|
||||
|
||||
Object r2 = service.multiCache(o1);
|
||||
Object r3 = service.multiCache(o1);
|
||||
|
||||
assertSame(r1, r2);
|
||||
assertSame(r1, r3);
|
||||
|
||||
assertNull(primary.get(o2));
|
||||
assertNull(secondary.get(o2));
|
||||
Object r4 = service.multiCache(o2);
|
||||
assertSame(r4, primary.get(o2).get());
|
||||
assertSame(r4, secondary.get(o2).get());
|
||||
}
|
||||
|
||||
public void testMultiEvict(CacheableService service) {
|
||||
Object o1 = new Object();
|
||||
|
||||
Object r1 = service.multiCache(o1);
|
||||
Object r2 = service.multiCache(o1);
|
||||
|
||||
Cache primary = cm.getCache("primary");
|
||||
Cache secondary = cm.getCache("secondary");
|
||||
|
||||
assertSame(r1, r2);
|
||||
assertSame(r1, primary.get(o1).get());
|
||||
assertSame(r1, secondary.get(o1).get());
|
||||
|
||||
service.multiEvict(o1);
|
||||
assertNull(primary.get(o1));
|
||||
assertNull(secondary.get(o1));
|
||||
|
||||
Object r3 = service.multiCache(o1);
|
||||
Object r4 = service.multiCache(o1);
|
||||
assertNotSame(r1, r3);
|
||||
assertSame(r3, r4);
|
||||
|
||||
assertSame(r3, primary.get(o1).get());
|
||||
assertSame(r4, secondary.get(o1).get());
|
||||
}
|
||||
|
||||
public void testMultiPut(CacheableService service) {
|
||||
Object o = Integer.valueOf(1);
|
||||
|
||||
Cache primary = cm.getCache("primary");
|
||||
Cache secondary = cm.getCache("secondary");
|
||||
|
||||
assertNull(primary.get(o));
|
||||
assertNull(secondary.get(o));
|
||||
Object r1 = service.multiUpdate(o);
|
||||
assertSame(r1, primary.get(o).get());
|
||||
assertSame(r1, secondary.get(o).get());
|
||||
|
||||
o = Integer.valueOf(2);
|
||||
assertNull(primary.get(o));
|
||||
assertNull(secondary.get(o));
|
||||
Object r2 = service.multiUpdate(o);
|
||||
assertSame(r2, primary.get(o).get());
|
||||
assertSame(r2, secondary.get(o).get());
|
||||
}
|
||||
|
||||
public void testMultiCacheAndEvict(CacheableService service) {
|
||||
String methodName = "multiCacheAndEvict";
|
||||
|
||||
Cache primary = cm.getCache("primary");
|
||||
Cache secondary = cm.getCache("secondary");
|
||||
Object key = Integer.valueOf(1);
|
||||
|
||||
secondary.put(key, key);
|
||||
|
||||
assertNull(secondary.get(methodName));
|
||||
assertSame(key, secondary.get(key).get());
|
||||
|
||||
Object r1 = service.multiCacheAndEvict(key);
|
||||
assertSame(r1, service.multiCacheAndEvict(key));
|
||||
|
||||
// assert the method name is used
|
||||
assertSame(r1, primary.get(methodName).get());
|
||||
assertNull(secondary.get(methodName));
|
||||
assertNull(secondary.get(key));
|
||||
}
|
||||
|
||||
public void testMultiConditionalCacheAndEvict(CacheableService service) {
|
||||
Cache primary = cm.getCache("primary");
|
||||
Cache secondary = cm.getCache("secondary");
|
||||
Object key = Integer.valueOf(1);
|
||||
|
||||
secondary.put(key, key);
|
||||
|
||||
assertNull(primary.get(key));
|
||||
assertSame(key, secondary.get(key).get());
|
||||
|
||||
Object r1 = service.multiConditionalCacheAndEvict(key);
|
||||
Object r3 = service.multiConditionalCacheAndEvict(key);
|
||||
|
||||
assertTrue(!r1.equals(r3));
|
||||
assertNull(primary.get(key));
|
||||
|
||||
Object key2 = Integer.valueOf(3);
|
||||
Object r2 = service.multiConditionalCacheAndEvict(key2);
|
||||
assertSame(r2, service.multiConditionalCacheAndEvict(key2));
|
||||
|
||||
// assert the method name is used
|
||||
assertSame(r2, primary.get(key2).get());
|
||||
assertNull(secondary.get(key2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCacheable() throws Exception {
|
||||
testCacheable(cs);
|
||||
|
|
@ -329,4 +452,54 @@ public abstract class AbstractAnnotationTests {
|
|||
public void testClassConditionalUpdate() {
|
||||
testConditionalCacheUpdate(ccs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiCache() {
|
||||
testMultiCache(cs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassMultiCache() {
|
||||
testMultiCache(ccs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiEvict() {
|
||||
testMultiEvict(cs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassMultiEvict() {
|
||||
testMultiEvict(ccs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiPut() {
|
||||
testMultiPut(cs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassMultiPut() {
|
||||
testMultiPut(ccs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiCacheAndEvict() {
|
||||
testMultiCacheAndEvict(cs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassMultiCacheAndEvict() {
|
||||
testMultiCacheAndEvict(ccs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiConditionalCacheAndEvict() {
|
||||
testMultiConditionalCacheAndEvict(cs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassMultiConditionalCacheAndEvict() {
|
||||
testMultiConditionalCacheAndEvict(ccs);
|
||||
}
|
||||
}
|
||||
|
|
@ -18,8 +18,9 @@ package org.springframework.cache.config;
|
|||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.springframework.cache.annotation.CacheDefinitions;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.CacheUpdate;
|
||||
import org.springframework.cache.annotation.CachePut;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
|
||||
/**
|
||||
|
|
@ -62,12 +63,12 @@ public class AnnotatedClassCacheableService implements CacheableService {
|
|||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@CacheUpdate("default")
|
||||
@CachePut("default")
|
||||
public Object update(Object arg1) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@CacheUpdate(value = "default", condition = "#arg.equals(3)")
|
||||
@CachePut(value = "default", condition = "#arg.equals(3)")
|
||||
public Object conditionalUpdate(Object arg) {
|
||||
return arg;
|
||||
}
|
||||
|
|
@ -88,4 +89,31 @@ public class AnnotatedClassCacheableService implements CacheableService {
|
|||
public Long throwUnchecked(Object arg1) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
// multi annotations
|
||||
|
||||
@CacheDefinitions(cacheable = { @Cacheable("primary"), @Cacheable("secondary") })
|
||||
public Object multiCache(Object arg1) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@CacheDefinitions(evict = { @CacheEvict("primary"), @CacheEvict(value = "secondary", key = "#p0") })
|
||||
public Object multiEvict(Object arg1) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@CacheDefinitions(cacheable = { @Cacheable(value = "primary", key = "#root.methodName") }, evict = { @CacheEvict("secondary") })
|
||||
public Object multiCacheAndEvict(Object arg1) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@CacheDefinitions(cacheable = { @Cacheable(value = "primary", condition = "#p0 == 3") }, evict = { @CacheEvict("secondary") })
|
||||
public Object multiConditionalCacheAndEvict(Object arg1) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@CacheDefinitions(put = { @CachePut("primary"), @CachePut("secondary") })
|
||||
public Object multiUpdate(Object arg1) {
|
||||
return arg1;
|
||||
}
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@ import org.springframework.cache.interceptor.CacheInterceptor;
|
|||
/**
|
||||
* @author Costin Leau
|
||||
*/
|
||||
public abstract class AbstractCacheAdviceNamespaceTests extends AbstractAnnotationTests {
|
||||
public class CacheAdviceNamespaceTests extends AbstractAnnotationTests {
|
||||
|
||||
|
||||
@Override
|
||||
|
|
@ -50,4 +50,14 @@ public interface CacheableService<T> {
|
|||
|
||||
T throwUnchecked(Object arg1);
|
||||
|
||||
// multi annotations
|
||||
T multiCache(Object arg1);
|
||||
|
||||
T multiEvict(Object arg1);
|
||||
|
||||
T multiCacheAndEvict(Object arg1);
|
||||
|
||||
T multiConditionalCacheAndEvict(Object arg1);
|
||||
|
||||
T multiUpdate(Object arg1);
|
||||
}
|
||||
|
|
@ -18,8 +18,9 @@ package org.springframework.cache.config;
|
|||
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.springframework.cache.annotation.CacheDefinitions;
|
||||
import org.springframework.cache.annotation.CacheEvict;
|
||||
import org.springframework.cache.annotation.CacheUpdate;
|
||||
import org.springframework.cache.annotation.CachePut;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
|
||||
/**
|
||||
|
|
@ -65,12 +66,12 @@ public class DefaultCacheableService implements CacheableService<Long> {
|
|||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@CacheUpdate("default")
|
||||
@CachePut("default")
|
||||
public Long update(Object arg1) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@CacheUpdate(value = "default", condition = "#arg.equals(3)")
|
||||
@CachePut(value = "default", condition = "#arg.equals(3)")
|
||||
public Long conditionalUpdate(Object arg) {
|
||||
return Long.valueOf(arg.toString());
|
||||
}
|
||||
|
|
@ -94,4 +95,31 @@ public class DefaultCacheableService implements CacheableService<Long> {
|
|||
public Long throwUnchecked(Object arg1) {
|
||||
throw new UnsupportedOperationException(arg1.toString());
|
||||
}
|
||||
|
||||
// multi annotations
|
||||
|
||||
@CacheDefinitions(cacheable = { @Cacheable("primary"), @Cacheable("secondary") })
|
||||
public Long multiCache(Object arg1) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@CacheDefinitions(evict = { @CacheEvict("primary"), @CacheEvict(value = "secondary", key = "#p0") })
|
||||
public Long multiEvict(Object arg1) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@CacheDefinitions(cacheable = { @Cacheable(value = "primary", key = "#root.methodName") }, evict = { @CacheEvict("secondary") })
|
||||
public Long multiCacheAndEvict(Object arg1) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@CacheDefinitions(cacheable = { @Cacheable(value = "primary", condition = "#p0 == 3") }, evict = { @CacheEvict("secondary") })
|
||||
public Long multiConditionalCacheAndEvict(Object arg1) {
|
||||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@CacheDefinitions(put = { @CachePut("primary"), @CachePut("secondary") })
|
||||
public Long multiUpdate(Object arg1) {
|
||||
return Long.valueOf(arg1.toString());
|
||||
}
|
||||
}
|
||||
|
|
@ -30,6 +30,8 @@
|
|||
<property name="caches">
|
||||
<set>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="primary"/>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="secondary"/>
|
||||
</set>
|
||||
</property>
|
||||
</bean>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@
|
|||
<property name="caches">
|
||||
<set>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="primary"/>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="secondary"/>
|
||||
</set>
|
||||
</property>
|
||||
</bean>
|
||||
|
|
|
|||
|
|
@ -20,6 +20,26 @@
|
|||
<cache:cache-evict method="invalidate" cache="default"/>
|
||||
<cache:cache-evict method="evict" key="#p0" cache="default"/>
|
||||
</cache:definitions>
|
||||
<cache:definitions cache="default">
|
||||
<cache:cache-put method="update"/>
|
||||
<cache:cache-put method="conditionalUpdate" condition="#arg.equals(3)"/>
|
||||
</cache:definitions>
|
||||
<cache:definitions method="mult*Cache">
|
||||
<cache:cacheable cache="primary"/>
|
||||
<cache:cacheable cache="secondary"/>
|
||||
</cache:definitions>
|
||||
<cache:definitions method="multiEvict">
|
||||
<cache:cache-evict cache="primary"/>
|
||||
<cache:cache-evict method="multiEvict" cache="secondary" key="#p0"/>
|
||||
</cache:definitions>
|
||||
<cache:definitions>
|
||||
<cache:cacheable method="multiCacheAndEvict" cache="primary" key="#root.methodName"/>
|
||||
<cache:cache-evict method="multiCacheAndEvict" cache="secondary"/>
|
||||
<cache:cacheable method="multiConditionalCacheAndEvict" cache="primary" condition="#p0 == 3"/>
|
||||
<cache:cache-evict method="multiConditionalCacheAndEvict" cache="secondary"/>
|
||||
<cache:cache-put method="multiUpdate" cache="primary"/>
|
||||
<cache:cache-put method="multiUpdate" cache="secondary"/>
|
||||
</cache:definitions>
|
||||
</cache:advice>
|
||||
|
||||
<cache:advice id="cacheAdviceClass" cache-manager="cacheManager" key-generator="keyGenerator">
|
||||
|
|
@ -35,6 +55,22 @@
|
|||
<cache:cache-evict method="invalidate" cache="default"/>
|
||||
<cache:cache-evict method="evict" key="#p0" cache="default"/>
|
||||
</cache:definitions>
|
||||
<cache:definitions cache="default">
|
||||
<cache:cache-put method="update"/>
|
||||
<cache:cache-put method="conditionalUpdate" condition="#arg.equals(3)"/>
|
||||
</cache:definitions>
|
||||
<cache:definitions>
|
||||
<cache:cacheable method="multiCache" cache="primary"/>
|
||||
<cache:cacheable method="multiCache" cache="secondary"/>
|
||||
<cache:cache-evict method="multiEvict" cache="primary"/>
|
||||
<cache:cache-evict method="multiEvict" cache="secondary" key="#p0"/>
|
||||
<cache:cacheable method="multiCacheAndEvict" cache="primary" key="#root.methodName"/>
|
||||
<cache:cache-evict method="multiCacheAndEvict" cache="secondary"/>
|
||||
<cache:cacheable method="multiConditionalCacheAndEvict" cache="primary" condition="#p0 == 3"/>
|
||||
<cache:cache-evict method="multiConditionalCacheAndEvict" cache="secondary"/>
|
||||
<cache:cache-put method="multiUpdate" cache="primary"/>
|
||||
<cache:cache-put method="multiUpdate" cache="secondary"/>
|
||||
</cache:definitions>
|
||||
</cache:advice>
|
||||
|
||||
<aop:config>
|
||||
|
|
@ -47,6 +83,8 @@
|
|||
<property name="caches">
|
||||
<set>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="primary"/>
|
||||
<bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="secondary"/>
|
||||
</set>
|
||||
</property>
|
||||
</bean>
|
||||
|
|
|
|||
Loading…
Reference in New Issue