Merge branch '3.2.x' into cleanup-3.2.x
* 3.2.x: Preparations for 3.2.2 Marked spring-web module as 'distributable' in order for session replication to work on Tomcat SQLErrorCodeSQLExceptionTranslator tries to find SQLException with actual error code, looping through the causes. Fixed setFavorPathExtension delegation code Added ContentNegotiationManager(Collection<ContentNegotiationStrategy>) constructor ConfigurationClassPostProcessor allows for overriding of scoped-proxy bean definitions Limit auto grow collection size when using SpEL Improve presentation of code blocks in Javadoc Polish @Configuration javadoc Update Quartz documentation to use FactoryBeans Support 'unless' expression for cache veto
This commit is contained in:
commit
5edd2ffa10
|
|
@ -817,6 +817,7 @@ configure(rootProject) {
|
|||
options.author = true
|
||||
options.header = rootProject.description
|
||||
options.overview = "src/api/overview.html"
|
||||
options.stylesheetFile = file("src/api/stylesheet.css")
|
||||
options.splitIndex = true
|
||||
options.links(project.ext.javadocLinks)
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -25,6 +25,7 @@ import org.springframework.cache.annotation.Caching;
|
|||
|
||||
/**
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Cacheable("default")
|
||||
public class AnnotatedClassCacheableService implements CacheableService<Object> {
|
||||
|
|
@ -40,6 +41,10 @@ public class AnnotatedClassCacheableService implements CacheableService<Object>
|
|||
return null;
|
||||
}
|
||||
|
||||
public Object unless(int arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
@CacheEvict("default")
|
||||
public void invalidate(Object arg1) {
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -20,6 +20,7 @@ package org.springframework.cache.config;
|
|||
* Basic service interface.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public interface CacheableService<T> {
|
||||
|
||||
|
|
@ -39,6 +40,8 @@ public interface CacheableService<T> {
|
|||
|
||||
T conditional(int field);
|
||||
|
||||
T unless(int arg);
|
||||
|
||||
T key(Object arg1, Object arg2);
|
||||
|
||||
T name(Object arg1);
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ import org.springframework.cache.annotation.Caching;
|
|||
* Simple cacheable service
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class DefaultCacheableService implements CacheableService<Long> {
|
||||
|
||||
|
|
@ -78,6 +79,12 @@ public class DefaultCacheableService implements CacheableService<Long> {
|
|||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "default", unless = "#result > 10")
|
||||
public Long unless(int arg) {
|
||||
return (long) arg;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "default", key = "#p0")
|
||||
public Long key(Object arg1, Object arg2) {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -32,6 +32,7 @@ import org.springframework.cache.Cache;
|
|||
* always causes the method to be invoked and its result to be placed into the cache.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
* @since 3.1
|
||||
*/
|
||||
@Target({ ElementType.METHOD, ElementType.TYPE })
|
||||
|
|
@ -58,4 +59,13 @@ public @interface CachePut {
|
|||
* <p>Default is "", meaning the method result is always cached.
|
||||
*/
|
||||
String condition() default "";
|
||||
|
||||
/**
|
||||
* Spring Expression Language (SpEL) attribute used to veto the cache update.
|
||||
* <p>Unlike {@link #condition()}, this expression is evaluated after the method
|
||||
* has been called and can therefore refer to the {@code result}. Default is "",
|
||||
* meaning that caching is never vetoed.
|
||||
* @since 3.2
|
||||
*/
|
||||
String unless() default "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -30,6 +30,7 @@ import java.lang.annotation.Target;
|
|||
* returned instance is used as the cache value.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
* @since 3.1
|
||||
*/
|
||||
@Target({ElementType.METHOD, ElementType.TYPE})
|
||||
|
|
@ -56,4 +57,13 @@ public @interface Cacheable {
|
|||
* <p>Default is "", meaning the method is always cached.
|
||||
*/
|
||||
String condition() default "";
|
||||
|
||||
/**
|
||||
* Spring Expression Language (SpEL) attribute used to veto method caching.
|
||||
* <p>Unlike {@link #condition()}, this expression is evaluated after the method
|
||||
* has been called and can therefore refer to the {@code result}. Default is "",
|
||||
* meaning that caching is never vetoed.
|
||||
* @since 3.2
|
||||
*/
|
||||
String unless() default "";
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -35,6 +35,7 @@ import org.springframework.util.ObjectUtils;
|
|||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
* @since 3.1
|
||||
*/
|
||||
@SuppressWarnings("serial")
|
||||
|
|
@ -82,6 +83,7 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
|||
CacheableOperation cuo = new CacheableOperation();
|
||||
cuo.setCacheNames(caching.value());
|
||||
cuo.setCondition(caching.condition());
|
||||
cuo.setUnless(caching.unless());
|
||||
cuo.setKey(caching.key());
|
||||
cuo.setName(ae.toString());
|
||||
return cuo;
|
||||
|
|
@ -102,6 +104,7 @@ public class SpringCacheAnnotationParser implements CacheAnnotationParser, Seria
|
|||
CachePutOperation cuo = new CachePutOperation();
|
||||
cuo.setCacheNames(caching.value());
|
||||
cuo.setCondition(caching.condition());
|
||||
cuo.setUnless(caching.unless());
|
||||
cuo.setKey(caching.key());
|
||||
cuo.setName(ae.toString());
|
||||
return cuo;
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -44,6 +44,7 @@ import org.w3c.dom.Element;
|
|||
* BeanDefinitionParser} for the {@code <tx:advice/>} tag.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
|
||||
|
||||
|
|
@ -54,7 +55,9 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
|
|||
*/
|
||||
private static class Props {
|
||||
|
||||
private String key, condition, method;
|
||||
private String key;
|
||||
private String condition;
|
||||
private String method;
|
||||
private String[] caches = null;
|
||||
|
||||
Props(Element root) {
|
||||
|
|
@ -70,13 +73,9 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
|
|||
|
||||
<T extends CacheOperation> T merge(Element element, ReaderContext readerCtx, T 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
|
||||
String[] localCaches = caches;
|
||||
if (StringUtils.hasText(cache)) {
|
||||
localCaches = StringUtils.commaDelimitedListToStringArray(cache.trim());
|
||||
} else {
|
||||
|
|
@ -84,17 +83,10 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
|
|||
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);
|
||||
|
||||
op.setKey(getAttributeValue(element, "key", this.key));
|
||||
op.setCondition(getAttributeValue(element, "condition", this.condition));
|
||||
|
||||
return op;
|
||||
}
|
||||
|
|
@ -165,7 +157,8 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
|
|||
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());
|
||||
CacheableOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CacheableOperation());
|
||||
op.setUnless(getAttributeValue(opElement, "unless", ""));
|
||||
|
||||
Collection<CacheOperation> col = cacheOpMap.get(nameHolder);
|
||||
if (col == null) {
|
||||
|
|
@ -207,7 +200,8 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
|
|||
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());
|
||||
CachePutOperation op = prop.merge(opElement, parserContext.getReaderContext(), new CachePutOperation());
|
||||
op.setUnless(getAttributeValue(opElement, "unless", ""));
|
||||
|
||||
Collection<CacheOperation> col = cacheOpMap.get(nameHolder);
|
||||
if (col == null) {
|
||||
|
|
@ -222,4 +216,14 @@ class CacheAdviceParser extends AbstractSingleBeanDefinitionParser {
|
|||
attributeSourceDefinition.getPropertyValues().add("nameMap", cacheOpMap);
|
||||
return attributeSourceDefinition;
|
||||
}
|
||||
|
||||
|
||||
private static String getAttributeValue(Element element, String attributeName, String defaultValue) {
|
||||
String attribute = element.getAttribute(attributeName);
|
||||
if(StringUtils.hasText(attribute)) {
|
||||
return attribute.trim();
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -56,6 +56,7 @@ import org.springframework.util.StringUtils;
|
|||
* @author Costin Leau
|
||||
* @author Juergen Hoeller
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
* @since 3.1
|
||||
*/
|
||||
public abstract class CacheAspectSupport implements InitializingBean {
|
||||
|
|
@ -212,7 +213,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
|
||||
retVal = invoker.invoke();
|
||||
|
||||
inspectAfterCacheEvicts(ops.get(EVICT));
|
||||
inspectAfterCacheEvicts(ops.get(EVICT), retVal);
|
||||
|
||||
if (!updates.isEmpty()) {
|
||||
update(updates, retVal);
|
||||
|
|
@ -225,14 +226,16 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
}
|
||||
|
||||
private void inspectBeforeCacheEvicts(Collection<CacheOperationContext> evictions) {
|
||||
inspectCacheEvicts(evictions, true);
|
||||
inspectCacheEvicts(evictions, true, ExpressionEvaluator.NO_RESULT);
|
||||
}
|
||||
|
||||
private void inspectAfterCacheEvicts(Collection<CacheOperationContext> evictions) {
|
||||
inspectCacheEvicts(evictions, false);
|
||||
private void inspectAfterCacheEvicts(Collection<CacheOperationContext> evictions,
|
||||
Object result) {
|
||||
inspectCacheEvicts(evictions, false, result);
|
||||
}
|
||||
|
||||
private void inspectCacheEvicts(Collection<CacheOperationContext> evictions, boolean beforeInvocation) {
|
||||
private void inspectCacheEvicts(Collection<CacheOperationContext> evictions,
|
||||
boolean beforeInvocation, Object result) {
|
||||
|
||||
if (!evictions.isEmpty()) {
|
||||
|
||||
|
|
@ -242,7 +245,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
CacheEvictOperation evictOp = (CacheEvictOperation) context.operation;
|
||||
|
||||
if (beforeInvocation == evictOp.isBeforeInvocation()) {
|
||||
if (context.isConditionPassing()) {
|
||||
if (context.isConditionPassing(result)) {
|
||||
// for each cache
|
||||
// lazy key initialization
|
||||
Object key = null;
|
||||
|
|
@ -278,7 +281,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
private CacheStatus inspectCacheables(Collection<CacheOperationContext> cacheables) {
|
||||
Map<CacheOperationContext, Object> cUpdates = new LinkedHashMap<CacheOperationContext, Object>(cacheables.size());
|
||||
|
||||
boolean updateRequire = false;
|
||||
boolean updateRequired = false;
|
||||
Object retVal = null;
|
||||
|
||||
if (!cacheables.isEmpty()) {
|
||||
|
|
@ -305,7 +308,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
boolean localCacheHit = false;
|
||||
|
||||
// check whether the cache needs to be inspected or not (the method will be invoked anyway)
|
||||
if (!updateRequire) {
|
||||
if (!updateRequired) {
|
||||
for (Cache cache : context.getCaches()) {
|
||||
Cache.ValueWrapper wrapper = cache.get(key);
|
||||
if (wrapper != null) {
|
||||
|
|
@ -317,7 +320,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
}
|
||||
|
||||
if (!localCacheHit) {
|
||||
updateRequire = true;
|
||||
updateRequired = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
|
@ -329,7 +332,7 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
|
||||
// return a status only if at least on cacheable matched
|
||||
if (atLeastOnePassed) {
|
||||
return new CacheStatus(cUpdates, updateRequire, retVal);
|
||||
return new CacheStatus(cUpdates, updateRequired, retVal);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -386,8 +389,11 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
|
||||
private void update(Map<CacheOperationContext, Object> updates, Object retVal) {
|
||||
for (Map.Entry<CacheOperationContext, Object> entry : updates.entrySet()) {
|
||||
for (Cache cache : entry.getKey().getCaches()) {
|
||||
cache.put(entry.getValue(), retVal);
|
||||
CacheOperationContext operationContext = entry.getKey();
|
||||
if(operationContext.canPutToCache(retVal)) {
|
||||
for (Cache cache : operationContext.getCaches()) {
|
||||
cache.put(entry.getValue(), retVal);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -427,30 +433,49 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
|
||||
private final CacheOperation operation;
|
||||
|
||||
private final Collection<Cache> caches;
|
||||
|
||||
private final Object target;
|
||||
|
||||
private final Method method;
|
||||
|
||||
private final Object[] args;
|
||||
|
||||
// context passed around to avoid multiple creations
|
||||
private final EvaluationContext evalContext;
|
||||
private final Object target;
|
||||
|
||||
private final Class<?> targetClass;
|
||||
|
||||
private final Collection<Cache> caches;
|
||||
|
||||
public CacheOperationContext(CacheOperation operation, Method method, Object[] args, Object target, Class<?> targetClass) {
|
||||
this.operation = operation;
|
||||
this.caches = CacheAspectSupport.this.getCaches(operation);
|
||||
this.target = target;
|
||||
this.method = method;
|
||||
this.args = args;
|
||||
|
||||
this.evalContext = evaluator.createEvaluationContext(caches, method, args, target, targetClass);
|
||||
this.target = target;
|
||||
this.targetClass = targetClass;
|
||||
this.caches = CacheAspectSupport.this.getCaches(operation);
|
||||
}
|
||||
|
||||
protected boolean isConditionPassing() {
|
||||
return isConditionPassing(ExpressionEvaluator.NO_RESULT);
|
||||
}
|
||||
|
||||
protected boolean isConditionPassing(Object result) {
|
||||
if (StringUtils.hasText(this.operation.getCondition())) {
|
||||
return evaluator.condition(this.operation.getCondition(), this.method, this.evalContext);
|
||||
EvaluationContext evaluationContext = createEvaluationContext(result);
|
||||
return evaluator.condition(this.operation.getCondition(), this.method,
|
||||
evaluationContext);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
protected boolean canPutToCache(Object value) {
|
||||
String unless = "";
|
||||
if (this.operation instanceof CacheableOperation) {
|
||||
unless = ((CacheableOperation) this.operation).getUnless();
|
||||
}
|
||||
else if (this.operation instanceof CachePutOperation) {
|
||||
unless = ((CachePutOperation) this.operation).getUnless();
|
||||
}
|
||||
if(StringUtils.hasText(unless)) {
|
||||
EvaluationContext evaluationContext = createEvaluationContext(value);
|
||||
return !evaluator.unless(unless, this.method, evaluationContext);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -461,11 +486,17 @@ public abstract class CacheAspectSupport implements InitializingBean {
|
|||
*/
|
||||
protected Object generateKey() {
|
||||
if (StringUtils.hasText(this.operation.getKey())) {
|
||||
return evaluator.key(this.operation.getKey(), this.method, this.evalContext);
|
||||
EvaluationContext evaluationContext = createEvaluationContext(ExpressionEvaluator.NO_RESULT);
|
||||
return evaluator.key(this.operation.getKey(), this.method, evaluationContext);
|
||||
}
|
||||
return keyGenerator.generate(this.target, this.method, this.args);
|
||||
}
|
||||
|
||||
private EvaluationContext createEvaluationContext(Object result) {
|
||||
return evaluator.createEvaluationContext(this.caches, this.method, this.args,
|
||||
this.target, this.targetClass, result);
|
||||
}
|
||||
|
||||
protected Collection<Cache> getCaches() {
|
||||
return this.caches;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -27,6 +27,7 @@ public class CacheEvictOperation extends CacheOperation {
|
|||
private boolean cacheWide = false;
|
||||
private boolean beforeInvocation = false;
|
||||
|
||||
|
||||
public void setCacheWide(boolean cacheWide) {
|
||||
this.cacheWide = cacheWide;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -119,10 +119,10 @@ public abstract class CacheOperation {
|
|||
result.append(this.name);
|
||||
result.append("] caches=");
|
||||
result.append(this.cacheNames);
|
||||
result.append(" | condition='");
|
||||
result.append(this.condition);
|
||||
result.append("' | key='");
|
||||
result.append(" | key='");
|
||||
result.append(this.key);
|
||||
result.append("' | condition='");
|
||||
result.append(this.condition);
|
||||
result.append("'");
|
||||
return result;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -20,8 +20,28 @@ package org.springframework.cache.interceptor;
|
|||
* Class describing a cache 'put' operation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
* @since 3.1
|
||||
*/
|
||||
public class CachePutOperation extends CacheOperation {
|
||||
|
||||
private String unless;
|
||||
|
||||
|
||||
public String getUnless() {
|
||||
return unless;
|
||||
}
|
||||
|
||||
public void setUnless(String unless) {
|
||||
this.unless = unless;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StringBuilder getOperationDescription() {
|
||||
StringBuilder sb = super.getOperationDescription();
|
||||
sb.append(" | unless='");
|
||||
sb.append(this.unless);
|
||||
sb.append("'");
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2011 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -20,8 +20,28 @@ package org.springframework.cache.interceptor;
|
|||
* Class describing a cache 'cacheable' operation.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
* @since 3.1
|
||||
*/
|
||||
public class CacheableOperation extends CacheOperation {
|
||||
|
||||
private String unless;
|
||||
|
||||
|
||||
public String getUnless() {
|
||||
return unless;
|
||||
}
|
||||
|
||||
public void setUnless(String unless) {
|
||||
this.unless = unless;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected StringBuilder getOperationDescription() {
|
||||
StringBuilder sb = super.getOperationDescription();
|
||||
sb.append(" | unless='");
|
||||
sb.append(this.unless);
|
||||
sb.append("'");
|
||||
return sb;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -35,49 +35,84 @@ import org.springframework.expression.spel.standard.SpelExpressionParser;
|
|||
* <p>Performs internal caching for performance reasons.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
* @since 3.1
|
||||
*/
|
||||
class ExpressionEvaluator {
|
||||
|
||||
public static final Object NO_RESULT = new Object();
|
||||
|
||||
private final SpelExpressionParser parser = new SpelExpressionParser();
|
||||
|
||||
// shared param discoverer since it caches data internally
|
||||
private final ParameterNameDiscoverer paramNameDiscoverer = new LocalVariableTableParameterNameDiscoverer();
|
||||
|
||||
private final Map<String, Expression> keyCache = new ConcurrentHashMap<String, Expression>(64);
|
||||
|
||||
private final Map<String, Expression> conditionCache = new ConcurrentHashMap<String, Expression>(64);
|
||||
|
||||
private final Map<String, Expression> keyCache = new ConcurrentHashMap<String, Expression>(64);
|
||||
private final Map<String, Expression> unlessCache = new ConcurrentHashMap<String, Expression>(64);
|
||||
|
||||
private final Map<String, Method> targetMethodCache = new ConcurrentHashMap<String, Method>(64);
|
||||
|
||||
|
||||
public EvaluationContext createEvaluationContext(
|
||||
Collection<Cache> caches, Method method, Object[] args, Object target, Class<?> targetClass) {
|
||||
|
||||
CacheExpressionRootObject rootObject =
|
||||
new CacheExpressionRootObject(caches, method, args, target, targetClass);
|
||||
return new LazyParamAwareEvaluationContext(rootObject,
|
||||
this.paramNameDiscoverer, method, args, targetClass, this.targetMethodCache);
|
||||
/**
|
||||
* Create an {@link EvaluationContext} without a return value.
|
||||
* @see #createEvaluationContext(Collection, Method, Object[], Object, Class, Object)
|
||||
*/
|
||||
public EvaluationContext createEvaluationContext(Collection<Cache> caches,
|
||||
Method method, Object[] args, Object target, Class<?> targetClass) {
|
||||
return createEvaluationContext(caches, method, args, target, targetClass,
|
||||
NO_RESULT);
|
||||
}
|
||||
|
||||
public boolean condition(String conditionExpression, Method method, EvaluationContext evalContext) {
|
||||
String key = toString(method, conditionExpression);
|
||||
Expression condExp = this.conditionCache.get(key);
|
||||
if (condExp == null) {
|
||||
condExp = this.parser.parseExpression(conditionExpression);
|
||||
this.conditionCache.put(key, condExp);
|
||||
/**
|
||||
* Create an {@link EvaluationContext}.
|
||||
*
|
||||
* @param caches the current caches
|
||||
* @param method the method
|
||||
* @param args the method arguments
|
||||
* @param target the target object
|
||||
* @param targetClass the target class
|
||||
* @param result the return value (can be {@code null}) or
|
||||
* {@link #NO_RESULT} if there is no return at this time
|
||||
* @return the evalulation context
|
||||
*/
|
||||
public EvaluationContext createEvaluationContext(Collection<Cache> caches,
|
||||
Method method, Object[] args, Object target, Class<?> targetClass,
|
||||
final Object result) {
|
||||
CacheExpressionRootObject rootObject = new CacheExpressionRootObject(caches,
|
||||
method, args, target, targetClass);
|
||||
LazyParamAwareEvaluationContext evaluationContext = new LazyParamAwareEvaluationContext(rootObject,
|
||||
this.paramNameDiscoverer, method, args, targetClass, this.targetMethodCache);
|
||||
if(result != NO_RESULT) {
|
||||
evaluationContext.setVariable("result", result);
|
||||
}
|
||||
return condExp.getValue(evalContext, boolean.class);
|
||||
return evaluationContext;
|
||||
}
|
||||
|
||||
public Object key(String keyExpression, Method method, EvaluationContext evalContext) {
|
||||
String key = toString(method, keyExpression);
|
||||
Expression keyExp = this.keyCache.get(key);
|
||||
if (keyExp == null) {
|
||||
keyExp = this.parser.parseExpression(keyExpression);
|
||||
this.keyCache.put(key, keyExp);
|
||||
return getExpression(this.keyCache, keyExpression, method).getValue(evalContext);
|
||||
}
|
||||
|
||||
public boolean condition(String conditionExpression, Method method, EvaluationContext evalContext) {
|
||||
return getExpression(this.conditionCache, conditionExpression, method).getValue(
|
||||
evalContext, boolean.class);
|
||||
}
|
||||
|
||||
public boolean unless(String unlessExpression, Method method, EvaluationContext evalContext) {
|
||||
return getExpression(this.unlessCache, unlessExpression, method).getValue(
|
||||
evalContext, boolean.class);
|
||||
}
|
||||
|
||||
private Expression getExpression(Map<String, Expression> cache, String expression, Method method) {
|
||||
String key = toString(method, expression);
|
||||
Expression rtn = cache.get(key);
|
||||
if (rtn == null) {
|
||||
rtn = this.parser.parseExpression(expression);
|
||||
cache.put(key, rtn);
|
||||
}
|
||||
return keyExp.getValue(evalContext);
|
||||
return rtn;
|
||||
}
|
||||
|
||||
private String toString(Method method, String expression) {
|
||||
|
|
|
|||
|
|
@ -68,8 +68,7 @@ import org.springframework.stereotype.Component;
|
|||
* <beans>
|
||||
* <context:annotation-config/>
|
||||
* <bean class="com.acme.AppConfig"/>
|
||||
* </beans>
|
||||
* }</pre>
|
||||
* </beans>}</pre>
|
||||
*
|
||||
* In the example above, {@code <context:annotation-config/>} is required in order to
|
||||
* enable {@link ConfigurationClassPostProcessor} and other annotation-related
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -256,7 +256,8 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
if (proxyMode != ScopedProxyMode.NO) {
|
||||
BeanDefinitionHolder proxyDef = ScopedProxyCreator.createScopedProxy(
|
||||
new BeanDefinitionHolder(beanDef, beanName), this.registry, proxyMode == ScopedProxyMode.TARGET_CLASS);
|
||||
beanDefToRegister = proxyDef.getBeanDefinition();
|
||||
beanDefToRegister =
|
||||
new ConfigurationClassBeanDefinition((RootBeanDefinition) proxyDef.getBeanDefinition(), configClass);
|
||||
}
|
||||
|
||||
if (logger.isDebugEnabled()) {
|
||||
|
|
@ -307,12 +308,17 @@ class ConfigurationClassBeanDefinitionReader {
|
|||
@SuppressWarnings("serial")
|
||||
private static class ConfigurationClassBeanDefinition extends RootBeanDefinition implements AnnotatedBeanDefinition {
|
||||
|
||||
private AnnotationMetadata annotationMetadata;
|
||||
private final AnnotationMetadata annotationMetadata;
|
||||
|
||||
public ConfigurationClassBeanDefinition(ConfigurationClass configClass) {
|
||||
this.annotationMetadata = configClass.getMetadata();
|
||||
}
|
||||
|
||||
public ConfigurationClassBeanDefinition(RootBeanDefinition original, ConfigurationClass configClass) {
|
||||
super(original);
|
||||
this.annotationMetadata = configClass.getMetadata();
|
||||
}
|
||||
|
||||
private ConfigurationClassBeanDefinition(ConfigurationClassBeanDefinition original) {
|
||||
super(original);
|
||||
this.annotationMetadata = original.annotationMetadata;
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@
|
|||
<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'.
|
||||
|
|
@ -52,7 +52,7 @@
|
|||
<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>
|
||||
|
|
@ -150,7 +150,7 @@
|
|||
<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>
|
||||
|
|
@ -160,7 +160,7 @@
|
|||
</tool:annotation>
|
||||
</xsd:appinfo>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
|
|
@ -194,16 +194,42 @@
|
|||
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 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="cacheable" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="basedefinitionType">
|
||||
<xsd:attribute name="unless" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The SpEL expression used to veto the method caching.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="cache-put" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
<xsd:extension base="basedefinitionType">
|
||||
<xsd:attribute name="unless" type="xsd:string" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
The SpEL expression used to veto the method caching.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="cache-evict" minOccurs="0" maxOccurs="unbounded">
|
||||
<xsd:complexType>
|
||||
<xsd:complexContent>
|
||||
|
|
@ -217,16 +243,16 @@
|
|||
<xsd:attribute name="before-invocation" type="xsd:boolean" use="optional">
|
||||
<xsd:annotation>
|
||||
<xsd:documentation><![CDATA[
|
||||
Whether the eviction should occur after the method is successfully
|
||||
Whether the eviction should occur after the method is successfully
|
||||
invoked (default) or before.]]></xsd:documentation>
|
||||
</xsd:annotation>
|
||||
</xsd:attribute>
|
||||
|
||||
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:choice>
|
||||
</xsd:sequence>
|
||||
</xsd:extension>
|
||||
</xsd:complexContent>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2010-2011 the original author or authors.
|
||||
* Copyright 2010-2013 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.
|
||||
|
|
@ -16,6 +16,8 @@
|
|||
|
||||
package org.springframework.cache.config;
|
||||
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.Collection;
|
||||
|
|
@ -33,6 +35,7 @@ import org.springframework.context.ApplicationContext;
|
|||
*
|
||||
* @author Costin Leau
|
||||
* @author Chris Beams
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public abstract class AbstractAnnotationTests {
|
||||
|
||||
|
|
@ -187,6 +190,15 @@ public abstract class AbstractAnnotationTests {
|
|||
assertSame(r3, r4);
|
||||
}
|
||||
|
||||
public void testUnlessExpression(CacheableService<?> service) throws Exception {
|
||||
Cache cache = cm.getCache("default");
|
||||
cache.clear();
|
||||
service.unless(10);
|
||||
service.unless(11);
|
||||
assertThat(cache.get(10).get(), equalTo((Object) 10L));
|
||||
assertThat(cache.get(11), nullValue());
|
||||
}
|
||||
|
||||
public void testKeyExpression(CacheableService<?> service) throws Exception {
|
||||
Object r1 = service.key(5, 1);
|
||||
Object r2 = service.key(5, 2);
|
||||
|
|
@ -441,6 +453,16 @@ public abstract class AbstractAnnotationTests {
|
|||
testConditionalExpression(cs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUnlessExpression() throws Exception {
|
||||
testUnlessExpression(cs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassCacheUnlessExpression() throws Exception {
|
||||
testUnlessExpression(cs);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testKeyExpression() throws Exception {
|
||||
testKeyExpression(cs);
|
||||
|
|
@ -618,4 +640,4 @@ public abstract class AbstractAnnotationTests {
|
|||
public void testClassMultiConditionalCacheAndEvict() {
|
||||
testMultiConditionalCacheAndEvict(ccs);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -25,6 +25,7 @@ import org.springframework.cache.annotation.Caching;
|
|||
|
||||
/**
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
@Cacheable("default")
|
||||
public class AnnotatedClassCacheableService implements CacheableService<Object> {
|
||||
|
|
@ -42,6 +43,12 @@ public class AnnotatedClassCacheableService implements CacheableService<Object>
|
|||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "default", unless = "#result > 10")
|
||||
public Object unless(int arg) {
|
||||
return arg;
|
||||
}
|
||||
|
||||
@Override
|
||||
@CacheEvict("default")
|
||||
public void invalidate(Object arg1) {
|
||||
|
|
@ -157,4 +164,4 @@ public class AnnotatedClassCacheableService implements CacheableService<Object>
|
|||
public Object multiUpdate(Object arg1) {
|
||||
return arg1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -20,6 +20,7 @@ package org.springframework.cache.config;
|
|||
* Basic service interface.
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public interface CacheableService<T> {
|
||||
|
||||
|
|
@ -39,6 +40,8 @@ public interface CacheableService<T> {
|
|||
|
||||
T conditional(int field);
|
||||
|
||||
T unless(int arg);
|
||||
|
||||
T key(Object arg1, Object arg2);
|
||||
|
||||
T name(Object arg1);
|
||||
|
|
@ -67,4 +70,4 @@ public interface CacheableService<T> {
|
|||
T multiConditionalCacheAndEvict(Object arg1);
|
||||
|
||||
T multiUpdate(Object arg1);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -27,6 +27,7 @@ import org.springframework.cache.annotation.Caching;
|
|||
* Simple cacheable service
|
||||
*
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class DefaultCacheableService implements CacheableService<Long> {
|
||||
|
||||
|
|
@ -78,6 +79,12 @@ public class DefaultCacheableService implements CacheableService<Long> {
|
|||
return counter.getAndIncrement();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "default", unless = "#result > 10")
|
||||
public Long unless(int arg) {
|
||||
return (long) arg;
|
||||
}
|
||||
|
||||
@Override
|
||||
@Cacheable(value = "default", key = "#p0")
|
||||
public Long key(Object arg1, Object arg2) {
|
||||
|
|
@ -163,4 +170,4 @@ public class DefaultCacheableService implements CacheableService<Long> {
|
|||
public Long multiUpdate(Object arg1) {
|
||||
return Long.valueOf(arg1.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2011 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -16,24 +16,39 @@
|
|||
|
||||
package org.springframework.cache.interceptor;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.springframework.cache.Cache;
|
||||
import org.springframework.cache.annotation.AnnotationCacheOperationSource;
|
||||
import org.springframework.cache.annotation.Cacheable;
|
||||
import org.springframework.cache.annotation.Caching;
|
||||
import org.springframework.cache.concurrent.ConcurrentMapCache;
|
||||
import org.springframework.expression.EvaluationContext;
|
||||
import org.springframework.expression.spel.standard.SpelExpressionParser;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
import edu.emory.mathcs.backport.java.util.Collections;
|
||||
|
||||
/**
|
||||
* @author Costin Leau
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
public class ExpressionEvalutatorTest {
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private ExpressionEvaluator eval = new ExpressionEvaluator();
|
||||
|
||||
private AnnotationCacheOperationSource source = new AnnotationCacheOperationSource();
|
||||
|
|
@ -59,6 +74,7 @@ public class ExpressionEvalutatorTest {
|
|||
}
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("unchecked")
|
||||
public void testMultipleCachingEval() throws Exception {
|
||||
AnnotatedClass target = new AnnotatedClass();
|
||||
Method method = ReflectionUtils.findMethod(AnnotatedClass.class, "multipleCaching", Object.class,
|
||||
|
|
@ -78,9 +94,41 @@ public class ExpressionEvalutatorTest {
|
|||
assertEquals(args[1], keyB);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withReturnValue() throws Exception {
|
||||
EvaluationContext context = createEvaluationContext("theResult");
|
||||
Object value = new SpelExpressionParser().parseExpression("#result").getValue(context);
|
||||
assertThat(value, equalTo((Object) "theResult"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withNullReturn() throws Exception {
|
||||
EvaluationContext context = createEvaluationContext(null);
|
||||
Object value = new SpelExpressionParser().parseExpression("#result").getValue(context);
|
||||
assertThat(value, nullValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void withoutReturnValue() throws Exception {
|
||||
EvaluationContext context = createEvaluationContext(ExpressionEvaluator.NO_RESULT);
|
||||
Object value = new SpelExpressionParser().parseExpression("#result").getValue(context);
|
||||
assertThat(value, nullValue());
|
||||
}
|
||||
|
||||
private EvaluationContext createEvaluationContext(Object result) {
|
||||
AnnotatedClass target = new AnnotatedClass();
|
||||
Method method = ReflectionUtils.findMethod(AnnotatedClass.class, "multipleCaching", Object.class,
|
||||
Object.class);
|
||||
Object[] args = new Object[] { new Object(), new Object() };
|
||||
@SuppressWarnings("unchecked")
|
||||
Collection<Cache> map = Collections.singleton(new ConcurrentMapCache("test"));
|
||||
EvaluationContext context = eval.createEvaluationContext(map, method, args, target, target.getClass(), result);
|
||||
return context;
|
||||
}
|
||||
|
||||
private static class AnnotatedClass {
|
||||
@Caching(cacheable = { @Cacheable(value = "test", key = "#a"), @Cacheable(value = "test", key = "#b") })
|
||||
public void multipleCaching(Object a, Object b) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
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
|
||||
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">
|
||||
|
||||
|
|
@ -11,6 +11,7 @@
|
|||
<cache:caching cache="default">
|
||||
<cache:cacheable method="cache"/>
|
||||
<cache:cacheable method="conditional" condition="#classField == 3"/>
|
||||
<cache:cacheable method="unless" unless="#result > 10"/>
|
||||
<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"/>
|
||||
|
|
@ -54,6 +55,7 @@
|
|||
<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="unless"/>
|
||||
<cache:cacheable method="null*"/>
|
||||
</cache:caching>
|
||||
<cache:caching>
|
||||
|
|
@ -80,9 +82,9 @@
|
|||
<cache:cache-evict method="multiConditionalCacheAndEvict" cache="secondary"/>
|
||||
<cache:cache-put method="multiUpdate" cache="primary"/>
|
||||
<cache:cache-put method="multiUpdate" cache="secondary"/>
|
||||
</cache:caching>
|
||||
</cache:caching>
|
||||
</cache:advice>
|
||||
|
||||
|
||||
<aop:config>
|
||||
<aop:advisor advice-ref="cacheAdviceInterface" pointcut="execution(* *..DefaultCacheableService.*(..))" order="1"/>
|
||||
<aop:advisor advice-ref="cacheAdviceClass" pointcut="execution(* *..AnnotatedClassCacheableService.*(..))" order="1"/>
|
||||
|
|
@ -98,12 +100,12 @@
|
|||
</set>
|
||||
</property>
|
||||
</bean>
|
||||
|
||||
|
||||
<bean id="keyGenerator" class="org.springframework.cache.config.SomeKeyGenerator"/>
|
||||
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2009 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -20,6 +20,7 @@ package org.springframework.expression.spel;
|
|||
* Configuration object for the SpEL expression parser.
|
||||
*
|
||||
* @author Juergen Hoeller
|
||||
* @author Phillip Webb
|
||||
* @since 3.0
|
||||
* @see org.springframework.expression.spel.standard.SpelExpressionParser#SpelExpressionParser(SpelParserConfiguration)
|
||||
*/
|
||||
|
|
@ -29,19 +30,52 @@ public class SpelParserConfiguration {
|
|||
|
||||
private final boolean autoGrowCollections;
|
||||
|
||||
private int maximumAutoGrowSize;
|
||||
|
||||
|
||||
/**
|
||||
* Create a new {@link SpelParserConfiguration} instance.
|
||||
* @param autoGrowNullReferences if null references should automatically grow
|
||||
* @param autoGrowCollections if collections should automatically grow
|
||||
* @see #SpelParserConfiguration(boolean, boolean, int)
|
||||
*/
|
||||
public SpelParserConfiguration(boolean autoGrowNullReferences, boolean autoGrowCollections) {
|
||||
this(autoGrowNullReferences, autoGrowCollections, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link SpelParserConfiguration} instance.
|
||||
* @param autoGrowNullReferences if null references should automatically grow
|
||||
* @param autoGrowCollections if collections should automatically grow
|
||||
* @param maximumAutoGrowSize the maximum size that the collection can auto grow
|
||||
*/
|
||||
public SpelParserConfiguration(boolean autoGrowNullReferences,
|
||||
boolean autoGrowCollections, int maximumAutoGrowSize) {
|
||||
this.autoGrowNullReferences = autoGrowNullReferences;
|
||||
this.autoGrowCollections = autoGrowCollections;
|
||||
this.maximumAutoGrowSize = maximumAutoGrowSize;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return {@code true} if {@code null} references should be automatically grown
|
||||
*/
|
||||
public boolean isAutoGrowNullReferences() {
|
||||
return this.autoGrowNullReferences;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {@code true} if collections should be automatically grown
|
||||
*/
|
||||
public boolean isAutoGrowCollections() {
|
||||
return this.autoGrowCollections;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maximum size that a collection can auto grow
|
||||
*/
|
||||
public int getMaximumAutoGrowSize() {
|
||||
return this.maximumAutoGrowSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -38,6 +38,7 @@ import org.springframework.expression.spel.support.ReflectivePropertyAccessor;
|
|||
* (lists/sets)/arrays
|
||||
*
|
||||
* @author Andy Clement
|
||||
* @author Phillip Webb
|
||||
* @since 3.0
|
||||
*/
|
||||
// TODO support multidimensional arrays
|
||||
|
|
@ -257,25 +258,20 @@ public class Indexer extends SpelNodeImpl {
|
|||
|
||||
private final boolean growCollection;
|
||||
|
||||
private int maximumSize;
|
||||
|
||||
CollectionIndexingValueRef(Collection collection, int index, TypeDescriptor collectionEntryTypeDescriptor,
|
||||
TypeConverter typeConverter, boolean growCollection) {
|
||||
TypeConverter typeConverter, boolean growCollection, int maximumSize) {
|
||||
this.collection = collection;
|
||||
this.index = index;
|
||||
this.collectionEntryTypeDescriptor = collectionEntryTypeDescriptor;
|
||||
this.typeConverter = typeConverter;
|
||||
this.growCollection = growCollection;
|
||||
this.maximumSize = maximumSize;
|
||||
}
|
||||
|
||||
public TypedValue getValue() {
|
||||
if (this.index >= this.collection.size()) {
|
||||
if (this.growCollection) {
|
||||
growCollection(this.collectionEntryTypeDescriptor, this.index, this.collection);
|
||||
}
|
||||
else {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS,
|
||||
this.collection.size(), this.index);
|
||||
}
|
||||
}
|
||||
growCollectionIfNecessary();
|
||||
if (this.collection instanceof List) {
|
||||
Object o = ((List) this.collection).get(this.index);
|
||||
return new TypedValue(o, this.collectionEntryTypeDescriptor.elementTypeDescriptor(o));
|
||||
|
|
@ -291,15 +287,7 @@ public class Indexer extends SpelNodeImpl {
|
|||
}
|
||||
|
||||
public void setValue(Object newValue) {
|
||||
if (this.index >= this.collection.size()) {
|
||||
if (this.growCollection) {
|
||||
growCollection(this.collectionEntryTypeDescriptor, this.index, this.collection);
|
||||
}
|
||||
else {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS,
|
||||
this.collection.size(), this.index);
|
||||
}
|
||||
}
|
||||
growCollectionIfNecessary();
|
||||
if (this.collection instanceof List) {
|
||||
List list = (List) this.collection;
|
||||
if (this.collectionEntryTypeDescriptor.getElementTypeDescriptor() != null) {
|
||||
|
|
@ -314,6 +302,36 @@ public class Indexer extends SpelNodeImpl {
|
|||
}
|
||||
}
|
||||
|
||||
private void growCollectionIfNecessary() {
|
||||
if (this.index >= this.collection.size()) {
|
||||
|
||||
if (!this.growCollection) {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.COLLECTION_INDEX_OUT_OF_BOUNDS,
|
||||
this.collection.size(), this.index);
|
||||
}
|
||||
|
||||
if(this.index >= this.maximumSize) {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.UNABLE_TO_GROW_COLLECTION);
|
||||
}
|
||||
|
||||
if (this.collectionEntryTypeDescriptor.getElementTypeDescriptor() == null) {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE);
|
||||
}
|
||||
|
||||
TypeDescriptor elementType = this.collectionEntryTypeDescriptor.getElementTypeDescriptor();
|
||||
try {
|
||||
int newElements = this.index - this.collection.size();
|
||||
while (newElements >= 0) {
|
||||
(this.collection).add(elementType.getType().newInstance());
|
||||
newElements--;
|
||||
}
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.UNABLE_TO_GROW_COLLECTION);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isWritable() {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -403,7 +421,8 @@ public class Indexer extends SpelNodeImpl {
|
|||
}
|
||||
else if (targetObject instanceof Collection) {
|
||||
return new CollectionIndexingValueRef((Collection<?>) targetObject, idx, targetObjectTypeDescriptor,
|
||||
state.getTypeConverter(), state.getConfiguration().isAutoGrowCollections());
|
||||
state.getTypeConverter(), state.getConfiguration().isAutoGrowCollections(),
|
||||
state.getConfiguration().getMaximumAutoGrowSize());
|
||||
}
|
||||
else if (targetObject instanceof String) {
|
||||
return new StringIndexingLValue((String) targetObject, idx, targetObjectTypeDescriptor);
|
||||
|
|
@ -421,32 +440,6 @@ public class Indexer extends SpelNodeImpl {
|
|||
targetObjectTypeDescriptor.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to grow the specified collection so that the specified index is valid.
|
||||
* @param targetType the type of the elements in the collection
|
||||
* @param index the index into the collection that needs to be valid
|
||||
* @param collection the collection to grow with elements
|
||||
*/
|
||||
private void growCollection(TypeDescriptor targetType, int index, Collection<Object> collection) {
|
||||
if (targetType.getElementTypeDescriptor() == null) {
|
||||
throw new SpelEvaluationException(getStartPosition(), SpelMessage.UNABLE_TO_GROW_COLLECTION_UNKNOWN_ELEMENT_TYPE);
|
||||
}
|
||||
TypeDescriptor elementType = targetType.getElementTypeDescriptor();
|
||||
Object newCollectionElement;
|
||||
try {
|
||||
int newElements = index - collection.size();
|
||||
while (newElements > 0) {
|
||||
collection.add(elementType.getType().newInstance());
|
||||
newElements--;
|
||||
}
|
||||
newCollectionElement = elementType.getType().newInstance();
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.UNABLE_TO_GROW_COLLECTION);
|
||||
}
|
||||
collection.add(newCollectionElement);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toStringAST() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -16,7 +16,14 @@
|
|||
|
||||
package org.springframework.expression.spel;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -24,7 +31,6 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import org.springframework.core.convert.TypeDescriptor;
|
||||
import org.springframework.expression.AccessException;
|
||||
import org.springframework.expression.BeanResolver;
|
||||
|
|
@ -48,6 +54,7 @@ import org.springframework.expression.spel.testresources.TestPerson;
|
|||
* @author Andy Clement
|
||||
* @author Mark Fisher
|
||||
* @author Sam Brannen
|
||||
* @author Phillip Webb
|
||||
* @since 3.0
|
||||
*/
|
||||
public class EvaluationTests extends ExpressionTestCase {
|
||||
|
|
@ -708,6 +715,23 @@ public class EvaluationTests extends ExpressionTestCase {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void limitCollectionGrowing() throws Exception {
|
||||
TestClass instance = new TestClass();
|
||||
StandardEvaluationContext ctx = new StandardEvaluationContext(instance);
|
||||
SpelExpressionParser parser = new SpelExpressionParser( new SpelParserConfiguration(true, true, 3));
|
||||
Expression expression = parser.parseExpression("foo[2]");
|
||||
expression.setValue(ctx, "2");
|
||||
assertThat(instance.getFoo().size(), equalTo(3));
|
||||
expression = parser.parseExpression("foo[3]");
|
||||
try {
|
||||
expression.setValue(ctx, "3");
|
||||
} catch(SpelEvaluationException see) {
|
||||
assertEquals(SpelMessage.UNABLE_TO_GROW_COLLECTION, see.getMessageCode());
|
||||
assertThat(instance.getFoo().size(), equalTo(3));
|
||||
}
|
||||
}
|
||||
|
||||
// For now I am making #this not assignable
|
||||
@Test
|
||||
public void increment01root() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -20,7 +20,6 @@ import java.lang.reflect.Constructor;
|
|||
import java.sql.BatchUpdateException;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.sql.DataSource;
|
||||
|
||||
import org.springframework.core.JdkVersion;
|
||||
|
|
@ -30,9 +29,9 @@ import org.springframework.dao.DataAccessException;
|
|||
import org.springframework.dao.DataAccessResourceFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.dao.DeadlockLoserDataAccessException;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.dao.PermissionDeniedDataAccessException;
|
||||
import org.springframework.dao.TransientDataAccessResourceException;
|
||||
import org.springframework.dao.DuplicateKeyException;
|
||||
import org.springframework.jdbc.BadSqlGrammarException;
|
||||
import org.springframework.jdbc.InvalidResultSetAccessException;
|
||||
|
||||
|
|
@ -201,20 +200,25 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep
|
|||
|
||||
// Check SQLErrorCodes with corresponding error code, if available.
|
||||
if (this.sqlErrorCodes != null) {
|
||||
String errorCode = null;
|
||||
String errorCode;
|
||||
if (this.sqlErrorCodes.isUseSqlStateForTranslation()) {
|
||||
errorCode = sqlEx.getSQLState();
|
||||
}
|
||||
else {
|
||||
errorCode = Integer.toString(sqlEx.getErrorCode());
|
||||
// Try to find SQLException with actual error code, looping through the causes.
|
||||
// E.g. applicable to java.sql.DataTruncation as of JDK 1.6.
|
||||
SQLException current = sqlEx;
|
||||
while (current.getErrorCode() == 0 && current.getCause() instanceof SQLException) {
|
||||
current = (SQLException) current.getCause();
|
||||
}
|
||||
errorCode = Integer.toString(current.getErrorCode());
|
||||
}
|
||||
|
||||
if (errorCode != null) {
|
||||
// Look for defined custom translations first.
|
||||
CustomSQLErrorCodesTranslation[] customTranslations = this.sqlErrorCodes.getCustomTranslations();
|
||||
if (customTranslations != null) {
|
||||
for (int i = 0; i < customTranslations.length; i++) {
|
||||
CustomSQLErrorCodesTranslation customTranslation = customTranslations[i];
|
||||
for (CustomSQLErrorCodesTranslation customTranslation : customTranslations) {
|
||||
if (Arrays.binarySearch(customTranslation.getErrorCodes(), errorCode) >= 0) {
|
||||
if (customTranslation.getExceptionClass() != null) {
|
||||
DataAccessException customException = createCustomException(
|
||||
|
|
@ -273,7 +277,7 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep
|
|||
|
||||
// We couldn't identify it more precisely - let's hand it over to the SQLState fallback translator.
|
||||
if (logger.isDebugEnabled()) {
|
||||
String codes = null;
|
||||
String codes;
|
||||
if (this.sqlErrorCodes != null && this.sqlErrorCodes.isUseSqlStateForTranslation()) {
|
||||
codes = "SQL state '" + sqlEx.getSQLState() + "', error code '" + sqlEx.getErrorCode();
|
||||
}
|
||||
|
|
@ -321,8 +325,8 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep
|
|||
try {
|
||||
int constructorType = 0;
|
||||
Constructor[] constructors = exceptionClass.getConstructors();
|
||||
for (int i = 0; i < constructors.length; i++) {
|
||||
Class[] parameterTypes = constructors[i].getParameterTypes();
|
||||
for (Constructor constructor : constructors) {
|
||||
Class[] parameterTypes = constructor.getParameterTypes();
|
||||
if (parameterTypes.length == 1 && parameterTypes[0].equals(String.class)) {
|
||||
if (constructorType < MESSAGE_ONLY_CONSTRUCTOR)
|
||||
constructorType = MESSAGE_ONLY_CONSTRUCTOR;
|
||||
|
|
@ -350,7 +354,7 @@ public class SQLErrorCodeSQLExceptionTranslator extends AbstractFallbackSQLExcep
|
|||
}
|
||||
|
||||
// invoke constructor
|
||||
Constructor exceptionConstructor = null;
|
||||
Constructor exceptionConstructor;
|
||||
switch (constructorType) {
|
||||
case MESSAGE_SQL_SQLEX_CONSTRUCTOR:
|
||||
Class[] messageAndSqlAndSqlExArgsClass = new Class[] {String.class, String.class, SQLException.class};
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -16,8 +16,9 @@
|
|||
|
||||
package org.springframework.jdbc.support;
|
||||
|
||||
import java.sql.SQLException;
|
||||
import java.sql.BatchUpdateException;
|
||||
import java.sql.DataTruncation;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import junit.framework.TestCase;
|
||||
|
||||
|
|
@ -33,6 +34,7 @@ import org.springframework.jdbc.InvalidResultSetAccessException;
|
|||
|
||||
/**
|
||||
* @author Rod Johnson
|
||||
* @author Juergen Hoeller
|
||||
*/
|
||||
public class SQLErrorCodeSQLExceptionTranslatorTests extends TestCase {
|
||||
|
||||
|
|
@ -92,13 +94,21 @@ public class SQLErrorCodeSQLExceptionTranslatorTests extends TestCase {
|
|||
SQLExceptionTranslator sext = new SQLErrorCodeSQLExceptionTranslator(ERROR_CODES);
|
||||
|
||||
SQLException badSqlEx = new SQLException("", "", 1);
|
||||
BatchUpdateException batchUdateEx = new BatchUpdateException();
|
||||
batchUdateEx.setNextException(badSqlEx);
|
||||
BadSqlGrammarException bsgex = (BadSqlGrammarException) sext.translate("task", "SQL", batchUdateEx);
|
||||
BatchUpdateException batchUpdateEx = new BatchUpdateException();
|
||||
batchUpdateEx.setNextException(badSqlEx);
|
||||
BadSqlGrammarException bsgex = (BadSqlGrammarException) sext.translate("task", "SQL", batchUpdateEx);
|
||||
assertEquals("SQL", bsgex.getSql());
|
||||
assertEquals(badSqlEx, bsgex.getSQLException());
|
||||
}
|
||||
|
||||
public void testDataTruncationTranslation() {
|
||||
SQLExceptionTranslator sext = new SQLErrorCodeSQLExceptionTranslator(ERROR_CODES);
|
||||
|
||||
SQLException dataAccessEx = new SQLException("", "", 5);
|
||||
DataTruncation dataTruncation = new DataTruncation(1, true, true, 1, 1, dataAccessEx);
|
||||
DataAccessResourceFailureException daex = (DataAccessResourceFailureException) sext.translate("task", "SQL", dataTruncation);
|
||||
assertEquals(dataTruncation, daex.getCause());
|
||||
}
|
||||
|
||||
@SuppressWarnings("serial")
|
||||
public void testCustomTranslateMethodTranslation() {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -18,6 +18,7 @@ package org.springframework.web.accept;
|
|||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
|
@ -55,6 +56,7 @@ public class ContentNegotiationManager implements ContentNegotiationStrategy, Me
|
|||
private final Set<MediaTypeFileExtensionResolver> fileExtensionResolvers =
|
||||
new LinkedHashSet<MediaTypeFileExtensionResolver>();
|
||||
|
||||
|
||||
/**
|
||||
* Create an instance with the given ContentNegotiationStrategy instances.
|
||||
* <p>Each instance is checked to see if it is also an implementation of
|
||||
|
|
@ -72,12 +74,29 @@ public class ContentNegotiationManager implements ContentNegotiationStrategy, Me
|
|||
}
|
||||
|
||||
/**
|
||||
* Create an instance with a {@link HeaderContentNegotiationStrategy}.
|
||||
* Create an instance with the given ContentNegotiationStrategy instances.
|
||||
* <p>Each instance is checked to see if it is also an implementation of
|
||||
* MediaTypeFileExtensionResolver, and if so it is registered as such.
|
||||
* @param strategies one more more ContentNegotiationStrategy instances
|
||||
*/
|
||||
public ContentNegotiationManager(Collection<ContentNegotiationStrategy> strategies) {
|
||||
Assert.notEmpty(strategies, "At least one ContentNegotiationStrategy is expected");
|
||||
this.contentNegotiationStrategies.addAll(strategies);
|
||||
for (ContentNegotiationStrategy strategy : this.contentNegotiationStrategies) {
|
||||
if (strategy instanceof MediaTypeFileExtensionResolver) {
|
||||
this.fileExtensionResolvers.add((MediaTypeFileExtensionResolver) strategy);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a default instance with a {@link HeaderContentNegotiationStrategy}.
|
||||
*/
|
||||
public ContentNegotiationManager() {
|
||||
this(new HeaderContentNegotiationStrategy());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Add MediaTypeFileExtensionResolver instances.
|
||||
* <p>Note that some {@link ContentNegotiationStrategy} implementations also
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -13,6 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.web.accept;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -22,7 +23,6 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.springframework.beans.factory.FactoryBean;
|
||||
|
|
@ -39,13 +39,13 @@ import org.springframework.web.context.ServletContextAware;
|
|||
* <p>By default strategies for checking the extension of the request path and
|
||||
* the {@code Accept} header are registered. The path extension check will perform
|
||||
* lookups through the {@link ServletContext} and the Java Activation Framework
|
||||
* (if present) unless {@linkplain #setMediaTypes(Properties) media types} are configured.
|
||||
* (if present) unless {@linkplain #setMediaTypes media types} are configured.
|
||||
*
|
||||
* @author Rossen Stoyanchev
|
||||
* @since 3.2
|
||||
*/
|
||||
public class ContentNegotiationManagerFactoryBean
|
||||
implements FactoryBean<ContentNegotiationManager>, InitializingBean, ServletContextAware {
|
||||
implements FactoryBean<ContentNegotiationManager>, ServletContextAware, InitializingBean {
|
||||
|
||||
private boolean favorPathExtension = true;
|
||||
|
||||
|
|
@ -65,6 +65,7 @@ public class ContentNegotiationManagerFactoryBean
|
|||
|
||||
private ServletContext servletContext;
|
||||
|
||||
|
||||
/**
|
||||
* Indicate whether the extension of the request path should be used to determine
|
||||
* the requested media type with the <em>highest priority</em>.
|
||||
|
|
@ -81,7 +82,6 @@ public class ContentNegotiationManagerFactoryBean
|
|||
* <p>When this mapping is not set or when an extension is not found, the Java
|
||||
* Action Framework, if available, may be used if enabled via
|
||||
* {@link #setFavorPathExtension(boolean)}.
|
||||
*
|
||||
* @see #addMediaType(String, MediaType)
|
||||
* @see #addMediaTypes(Map)
|
||||
*/
|
||||
|
|
@ -121,9 +121,8 @@ public class ContentNegotiationManagerFactoryBean
|
|||
* to map from file extensions to media types. This is used only when
|
||||
* {@link #setFavorPathExtension(boolean)} is set to {@code true}.
|
||||
* <p>The default value is {@code true}.
|
||||
*
|
||||
* @see #parameterName
|
||||
* @see #setMediaTypes(Properties)
|
||||
* @see #setParameterName
|
||||
* @see #setMediaTypes
|
||||
*/
|
||||
public void setUseJaf(boolean useJaf) {
|
||||
this.useJaf = useJaf;
|
||||
|
|
@ -138,8 +137,7 @@ public class ContentNegotiationManagerFactoryBean
|
|||
* {@code "application/pdf"} regardless of the {@code Accept} header.
|
||||
* <p>To use this option effectively you must also configure the MediaType
|
||||
* type mappings via {@link #setMediaTypes(Properties)}.
|
||||
*
|
||||
* @see #setParameterName(String)
|
||||
* @see #setParameterName
|
||||
*/
|
||||
public void setFavorParameter(boolean favorParameter) {
|
||||
this.favorParameter = favorParameter;
|
||||
|
|
@ -180,7 +178,8 @@ public class ContentNegotiationManagerFactoryBean
|
|||
this.servletContext = servletContext;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
List<ContentNegotiationStrategy> strategies = new ArrayList<ContentNegotiationStrategy>();
|
||||
|
||||
if (this.favorPathExtension) {
|
||||
|
|
@ -210,8 +209,12 @@ public class ContentNegotiationManagerFactoryBean
|
|||
strategies.add(new FixedContentNegotiationStrategy(this.defaultContentType));
|
||||
}
|
||||
|
||||
ContentNegotiationStrategy[] array = strategies.toArray(new ContentNegotiationStrategy[strategies.size()]);
|
||||
this.contentNegotiationManager = new ContentNegotiationManager(array);
|
||||
this.contentNegotiationManager = new ContentNegotiationManager(strategies);
|
||||
}
|
||||
|
||||
|
||||
public ContentNegotiationManager getObject() {
|
||||
return this.contentNegotiationManager;
|
||||
}
|
||||
|
||||
public Class<?> getObjectType() {
|
||||
|
|
@ -222,8 +225,4 @@ public class ContentNegotiationManagerFactoryBean
|
|||
return true;
|
||||
}
|
||||
|
||||
public ContentNegotiationManager getObject() throws Exception {
|
||||
return this.contentNegotiationManager;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,5 +5,6 @@
|
|||
version="3.0" metadata-complete="true">
|
||||
|
||||
<name>spring_web</name>
|
||||
<distributable/>
|
||||
|
||||
</web-fragment>
|
||||
</web-fragment>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2002-2012 the original author or authors.
|
||||
* Copyright 2002-2013 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.
|
||||
|
|
@ -25,7 +25,6 @@ import java.util.Locale;
|
|||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.activation.FileTypeMap;
|
||||
import javax.servlet.ServletContext;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
|
|
@ -33,6 +32,7 @@ import javax.servlet.http.HttpServletResponse;
|
|||
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
|
||||
import org.springframework.beans.factory.BeanFactoryUtils;
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.core.OrderComparator;
|
||||
|
|
@ -95,7 +95,7 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
|||
|
||||
private ContentNegotiationManager contentNegotiationManager;
|
||||
|
||||
private ContentNegotiationManagerFactoryBean cnManagerFactoryBean = new ContentNegotiationManagerFactoryBean();
|
||||
private final ContentNegotiationManagerFactoryBean cnManagerFactoryBean = new ContentNegotiationManagerFactoryBean();
|
||||
|
||||
private boolean useNotAcceptableStatusCode = false;
|
||||
|
||||
|
|
@ -104,10 +104,6 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
|||
private List<ViewResolver> viewResolvers;
|
||||
|
||||
|
||||
public ContentNegotiatingViewResolver() {
|
||||
super();
|
||||
}
|
||||
|
||||
public void setOrder(int order) {
|
||||
this.order = order;
|
||||
}
|
||||
|
|
@ -118,7 +114,9 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
|||
|
||||
/**
|
||||
* Set the {@link ContentNegotiationManager} to use to determine requested media types.
|
||||
* If not set, the default constructor is used.
|
||||
* <p>If not set, ContentNegotiationManager's default constructor will be used,
|
||||
* applying a {@link org.springframework.web.accept.HeaderContentNegotiationStrategy}.
|
||||
* @see ContentNegotiationManager#ContentNegotiationManager()
|
||||
*/
|
||||
public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) {
|
||||
this.contentNegotiationManager = contentNegotiationManager;
|
||||
|
|
@ -130,18 +128,16 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
|||
* <p>For instance, when this flag is {@code true} (the default), a request for {@code /hotels.pdf}
|
||||
* will result in an {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the
|
||||
* browser-defined {@code text/html,application/xhtml+xml}.
|
||||
*
|
||||
* @deprecated use {@link #setContentNegotiationManager(ContentNegotiationManager)}
|
||||
*/
|
||||
@Deprecated
|
||||
public void setFavorPathExtension(boolean favorPathExtension) {
|
||||
this.cnManagerFactoryBean.setFavorParameter(favorPathExtension);
|
||||
this.cnManagerFactoryBean.setFavorPathExtension(favorPathExtension);
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate whether to use the Java Activation Framework to map from file extensions to media types.
|
||||
* <p>Default is {@code true}, i.e. the Java Activation Framework is used (if available).
|
||||
*
|
||||
* @deprecated use {@link #setContentNegotiationManager(ContentNegotiationManager)}
|
||||
*/
|
||||
@Deprecated
|
||||
|
|
@ -155,7 +151,6 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
|||
* <p>For instance, when this flag is {@code true}, a request for {@code /hotels?format=pdf} will result
|
||||
* in an {@code AbstractPdfView} being resolved, while the {@code Accept} header can be the browser-defined
|
||||
* {@code text/html,application/xhtml+xml}.
|
||||
*
|
||||
* @deprecated use {@link #setContentNegotiationManager(ContentNegotiationManager)}
|
||||
*/
|
||||
@Deprecated
|
||||
|
|
@ -166,7 +161,6 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
|||
/**
|
||||
* Set the parameter name that can be used to determine the requested media type if the {@link
|
||||
* #setFavorParameter} property is {@code true}. The default parameter name is {@code format}.
|
||||
*
|
||||
* @deprecated use {@link #setContentNegotiationManager(ContentNegotiationManager)}
|
||||
*/
|
||||
@Deprecated
|
||||
|
|
@ -179,7 +173,6 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
|||
* <p>If set to {@code true}, this view resolver will only refer to the file extension and/or
|
||||
* parameter, as indicated by the {@link #setFavorPathExtension favorPathExtension} and
|
||||
* {@link #setFavorParameter favorParameter} properties.
|
||||
*
|
||||
* @deprecated use {@link #setContentNegotiationManager(ContentNegotiationManager)}
|
||||
*/
|
||||
@Deprecated
|
||||
|
|
@ -191,7 +184,6 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
|||
* Set the mapping from file extensions to media types.
|
||||
* <p>When this mapping is not set or when an extension is not present, this view resolver
|
||||
* will fall back to using a {@link FileTypeMap} when the Java Action Framework is available.
|
||||
*
|
||||
* @deprecated use {@link #setContentNegotiationManager(ContentNegotiationManager)}
|
||||
*/
|
||||
@Deprecated
|
||||
|
|
@ -207,7 +199,6 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
|||
* Set the default content type.
|
||||
* <p>This content type will be used when file extension, parameter, nor {@code Accept}
|
||||
* header define a content-type, either through being disabled or empty.
|
||||
*
|
||||
* @deprecated use {@link #setContentNegotiationManager(ContentNegotiationManager)}
|
||||
*/
|
||||
@Deprecated
|
||||
|
|
@ -275,7 +266,7 @@ public class ContentNegotiatingViewResolver extends WebApplicationObjectSupport
|
|||
this.cnManagerFactoryBean.setServletContext(servletContext);
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
public void afterPropertiesSet() {
|
||||
if (this.contentNegotiationManager == null) {
|
||||
this.cnManagerFactoryBean.afterPropertiesSet();
|
||||
this.contentNegotiationManager = this.cnManagerFactoryBean.getObject();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,541 @@
|
|||
/* Javadoc style sheet */
|
||||
|
||||
/*
|
||||
Overall document style
|
||||
*/
|
||||
body {
|
||||
background-color:#ffffff;
|
||||
color:#353833;
|
||||
font-family:Arial, Helvetica, sans-serif;
|
||||
font-size:76%;
|
||||
margin:0;
|
||||
}
|
||||
a:link, a:visited {
|
||||
text-decoration:none;
|
||||
color:#4c6b87;
|
||||
}
|
||||
a:hover, a:focus {
|
||||
text-decoration:none;
|
||||
color:#bb7a2a;
|
||||
}
|
||||
a:active {
|
||||
text-decoration:none;
|
||||
color:#4c6b87;
|
||||
}
|
||||
a[name] {
|
||||
color:#353833;
|
||||
}
|
||||
a[name]:hover {
|
||||
text-decoration:none;
|
||||
color:#353833;
|
||||
}
|
||||
pre {
|
||||
font-size:1.3em;
|
||||
}
|
||||
h1 {
|
||||
font-size:1.8em;
|
||||
}
|
||||
h2 {
|
||||
font-size:1.5em;
|
||||
}
|
||||
h3 {
|
||||
font-size:1.4em;
|
||||
}
|
||||
h4 {
|
||||
font-size:1.3em;
|
||||
}
|
||||
h5 {
|
||||
font-size:1.2em;
|
||||
}
|
||||
h6 {
|
||||
font-size:1.1em;
|
||||
}
|
||||
ul {
|
||||
list-style-type:disc;
|
||||
}
|
||||
code, tt {
|
||||
font-size:1.2em;
|
||||
}
|
||||
dt code {
|
||||
font-size:1.2em;
|
||||
}
|
||||
table tr td dt code {
|
||||
font-size:1.2em;
|
||||
vertical-align:top;
|
||||
}
|
||||
sup {
|
||||
font-size:.6em;
|
||||
}
|
||||
/*
|
||||
Document title and Copyright styles
|
||||
*/
|
||||
.clear {
|
||||
clear:both;
|
||||
height:0px;
|
||||
overflow:hidden;
|
||||
}
|
||||
.aboutLanguage {
|
||||
float:right;
|
||||
padding:0px 21px;
|
||||
font-size:.8em;
|
||||
z-index:200;
|
||||
margin-top:-7px;
|
||||
}
|
||||
.legalCopy {
|
||||
margin-left:.5em;
|
||||
}
|
||||
.bar a, .bar a:link, .bar a:visited, .bar a:active {
|
||||
color:#FFFFFF;
|
||||
text-decoration:none;
|
||||
}
|
||||
.bar a:hover, .bar a:focus {
|
||||
color:#bb7a2a;
|
||||
}
|
||||
.tab {
|
||||
background-color:#0066FF;
|
||||
background-image:url(resources/titlebar.gif);
|
||||
background-position:left top;
|
||||
background-repeat:no-repeat;
|
||||
color:#ffffff;
|
||||
padding:8px;
|
||||
width:5em;
|
||||
font-weight:bold;
|
||||
}
|
||||
/*
|
||||
Navigation bar styles
|
||||
*/
|
||||
.bar {
|
||||
background-image:url(resources/background.gif);
|
||||
background-repeat:repeat-x;
|
||||
color:#FFFFFF;
|
||||
padding:.8em .5em .4em .8em;
|
||||
height:auto;/*height:1.8em;*/
|
||||
font-size:1em;
|
||||
margin:0;
|
||||
}
|
||||
.topNav {
|
||||
background-image:url(resources/background.gif);
|
||||
background-repeat:repeat-x;
|
||||
color:#FFFFFF;
|
||||
float:left;
|
||||
padding:0;
|
||||
width:100%;
|
||||
clear:right;
|
||||
height:2.8em;
|
||||
padding-top:10px;
|
||||
overflow:hidden;
|
||||
}
|
||||
.bottomNav {
|
||||
margin-top:10px;
|
||||
background-image:url(resources/background.gif);
|
||||
background-repeat:repeat-x;
|
||||
color:#FFFFFF;
|
||||
float:left;
|
||||
padding:0;
|
||||
width:100%;
|
||||
clear:right;
|
||||
height:2.8em;
|
||||
padding-top:10px;
|
||||
overflow:hidden;
|
||||
}
|
||||
.subNav {
|
||||
background-color:#dee3e9;
|
||||
border-bottom:1px solid #9eadc0;
|
||||
float:left;
|
||||
width:100%;
|
||||
overflow:hidden;
|
||||
}
|
||||
.subNav div {
|
||||
clear:left;
|
||||
float:left;
|
||||
padding:0 0 5px 6px;
|
||||
}
|
||||
ul.navList, ul.subNavList {
|
||||
float:left;
|
||||
margin:0 25px 0 0;
|
||||
padding:0;
|
||||
}
|
||||
ul.navList li{
|
||||
list-style:none;
|
||||
float:left;
|
||||
padding:3px 6px;
|
||||
}
|
||||
ul.subNavList li{
|
||||
list-style:none;
|
||||
float:left;
|
||||
font-size:90%;
|
||||
}
|
||||
.topNav a:link, .topNav a:active, .topNav a:visited, .bottomNav a:link, .bottomNav a:active, .bottomNav a:visited {
|
||||
color:#FFFFFF;
|
||||
text-decoration:none;
|
||||
}
|
||||
.topNav a:hover, .bottomNav a:hover {
|
||||
text-decoration:none;
|
||||
color:#bb7a2a;
|
||||
}
|
||||
.navBarCell1Rev {
|
||||
background-image:url(resources/tab.gif);
|
||||
background-color:#a88834;
|
||||
color:#FFFFFF;
|
||||
margin: auto 5px;
|
||||
border:1px solid #c9aa44;
|
||||
}
|
||||
/*
|
||||
Page header and footer styles
|
||||
*/
|
||||
.header, .footer {
|
||||
clear:both;
|
||||
margin:0 20px;
|
||||
padding:5px 0 0 0;
|
||||
}
|
||||
.indexHeader {
|
||||
margin:10px;
|
||||
position:relative;
|
||||
}
|
||||
.indexHeader h1 {
|
||||
font-size:1.3em;
|
||||
}
|
||||
.title {
|
||||
color:#2c4557;
|
||||
margin:10px 0;
|
||||
}
|
||||
.subTitle {
|
||||
margin:5px 0 0 0;
|
||||
}
|
||||
.header ul {
|
||||
margin:0 0 25px 0;
|
||||
padding:0;
|
||||
}
|
||||
.footer ul {
|
||||
margin:20px 0 5px 0;
|
||||
}
|
||||
.header ul li, .footer ul li {
|
||||
list-style:none;
|
||||
font-size:1.2em;
|
||||
}
|
||||
/*
|
||||
Heading styles
|
||||
*/
|
||||
div.details ul.blockList ul.blockList ul.blockList li.blockList h4, div.details ul.blockList ul.blockList ul.blockListLast li.blockList h4 {
|
||||
background-color:#dee3e9;
|
||||
border-top:1px solid #9eadc0;
|
||||
border-bottom:1px solid #9eadc0;
|
||||
margin:0 0 6px -8px;
|
||||
padding:2px 5px;
|
||||
}
|
||||
ul.blockList ul.blockList ul.blockList li.blockList h3 {
|
||||
background-color:#dee3e9;
|
||||
border-top:1px solid #9eadc0;
|
||||
border-bottom:1px solid #9eadc0;
|
||||
margin:0 0 6px -8px;
|
||||
padding:2px 5px;
|
||||
}
|
||||
ul.blockList ul.blockList li.blockList h3 {
|
||||
padding:0;
|
||||
margin:15px 0;
|
||||
}
|
||||
ul.blockList li.blockList h2 {
|
||||
padding:0px 0 20px 0;
|
||||
}
|
||||
/*
|
||||
Page layout container styles
|
||||
*/
|
||||
.contentContainer, .sourceContainer, .classUseContainer, .serializedFormContainer, .constantValuesContainer {
|
||||
clear:both;
|
||||
padding:10px 20px;
|
||||
position:relative;
|
||||
}
|
||||
.indexContainer {
|
||||
margin:10px;
|
||||
position:relative;
|
||||
font-size:1.0em;
|
||||
}
|
||||
.indexContainer h2 {
|
||||
font-size:1.1em;
|
||||
padding:0 0 3px 0;
|
||||
}
|
||||
.indexContainer ul {
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
.indexContainer ul li {
|
||||
list-style:none;
|
||||
}
|
||||
.contentContainer .description dl dt, .contentContainer .details dl dt, .serializedFormContainer dl dt {
|
||||
font-size:1.1em;
|
||||
font-weight:bold;
|
||||
margin:10px 0 0 0;
|
||||
color:#4E4E4E;
|
||||
}
|
||||
.contentContainer .description dl dd, .contentContainer .details dl dd, .serializedFormContainer dl dd {
|
||||
margin:10px 0 10px 20px;
|
||||
}
|
||||
.serializedFormContainer dl.nameValue dt {
|
||||
margin-left:1px;
|
||||
font-size:1.1em;
|
||||
display:inline;
|
||||
font-weight:bold;
|
||||
}
|
||||
.serializedFormContainer dl.nameValue dd {
|
||||
margin:0 0 0 1px;
|
||||
font-size:1.1em;
|
||||
display:inline;
|
||||
}
|
||||
/*
|
||||
List styles
|
||||
*/
|
||||
ul.horizontal li {
|
||||
display:inline;
|
||||
font-size:0.9em;
|
||||
}
|
||||
ul.inheritance {
|
||||
margin:0;
|
||||
padding:0;
|
||||
}
|
||||
ul.inheritance li {
|
||||
display:inline;
|
||||
list-style:none;
|
||||
}
|
||||
ul.inheritance li ul.inheritance {
|
||||
margin-left:15px;
|
||||
padding-left:15px;
|
||||
padding-top:1px;
|
||||
}
|
||||
ul.blockList, ul.blockListLast {
|
||||
margin:10px 0 10px 0;
|
||||
padding:0;
|
||||
}
|
||||
ul.blockList li.blockList, ul.blockListLast li.blockList {
|
||||
list-style:none;
|
||||
margin-bottom:25px;
|
||||
}
|
||||
ul.blockList ul.blockList li.blockList, ul.blockList ul.blockListLast li.blockList {
|
||||
padding:0px 20px 5px 10px;
|
||||
border:1px solid #9eadc0;
|
||||
background-color:#f9f9f9;
|
||||
}
|
||||
ul.blockList ul.blockList ul.blockList li.blockList, ul.blockList ul.blockList ul.blockListLast li.blockList {
|
||||
padding:0 0 5px 8px;
|
||||
background-color:#ffffff;
|
||||
border:1px solid #9eadc0;
|
||||
border-top:none;
|
||||
}
|
||||
ul.blockList ul.blockList ul.blockList ul.blockList li.blockList {
|
||||
margin-left:0;
|
||||
padding-left:0;
|
||||
padding-bottom:15px;
|
||||
border:none;
|
||||
border-bottom:1px solid #9eadc0;
|
||||
}
|
||||
ul.blockList ul.blockList ul.blockList ul.blockList li.blockListLast {
|
||||
list-style:none;
|
||||
border-bottom:none;
|
||||
padding-bottom:0;
|
||||
}
|
||||
table tr td dl, table tr td dl dt, table tr td dl dd {
|
||||
margin-top:0;
|
||||
margin-bottom:1px;
|
||||
}
|
||||
/*
|
||||
Table styles
|
||||
*/
|
||||
.contentContainer table, .classUseContainer table, .constantValuesContainer table {
|
||||
border-bottom:1px solid #9eadc0;
|
||||
width:100%;
|
||||
}
|
||||
.contentContainer ul li table, .classUseContainer ul li table, .constantValuesContainer ul li table {
|
||||
width:100%;
|
||||
}
|
||||
.contentContainer .description table, .contentContainer .details table {
|
||||
border-bottom:none;
|
||||
}
|
||||
.contentContainer ul li table th.colOne, .contentContainer ul li table th.colFirst, .contentContainer ul li table th.colLast, .classUseContainer ul li table th, .constantValuesContainer ul li table th, .contentContainer ul li table td.colOne, .contentContainer ul li table td.colFirst, .contentContainer ul li table td.colLast, .classUseContainer ul li table td, .constantValuesContainer ul li table td{
|
||||
vertical-align:top;
|
||||
padding-right:20px;
|
||||
}
|
||||
.contentContainer ul li table th.colLast, .classUseContainer ul li table th.colLast,.constantValuesContainer ul li table th.colLast,
|
||||
.contentContainer ul li table td.colLast, .classUseContainer ul li table td.colLast,.constantValuesContainer ul li table td.colLast,
|
||||
.contentContainer ul li table th.colOne, .classUseContainer ul li table th.colOne,
|
||||
.contentContainer ul li table td.colOne, .classUseContainer ul li table td.colOne {
|
||||
padding-right:3px;
|
||||
}
|
||||
.overviewSummary caption, .packageSummary caption, .contentContainer ul.blockList li.blockList caption, .summary caption, .classUseContainer caption, .constantValuesContainer caption {
|
||||
position:relative;
|
||||
text-align:left;
|
||||
background-repeat:no-repeat;
|
||||
color:#FFFFFF;
|
||||
font-weight:bold;
|
||||
clear:none;
|
||||
overflow:hidden;
|
||||
padding:0px;
|
||||
margin:0px;
|
||||
}
|
||||
caption a:link, caption a:hover, caption a:active, caption a:visited {
|
||||
color:#FFFFFF;
|
||||
}
|
||||
.overviewSummary caption span, .packageSummary caption span, .contentContainer ul.blockList li.blockList caption span, .summary caption span, .classUseContainer caption span, .constantValuesContainer caption span {
|
||||
white-space:nowrap;
|
||||
padding-top:8px;
|
||||
padding-left:8px;
|
||||
display:block;
|
||||
float:left;
|
||||
background-image:url(resources/titlebar.gif);
|
||||
height:18px;
|
||||
}
|
||||
.contentContainer ul.blockList li.blockList caption span.activeTableTab span {
|
||||
white-space:nowrap;
|
||||
padding-top:8px;
|
||||
padding-left:8px;
|
||||
display:block;
|
||||
float:left;
|
||||
background-image:url(resources/activetitlebar.gif);
|
||||
height:18px;
|
||||
}
|
||||
.contentContainer ul.blockList li.blockList caption span.tableTab span {
|
||||
white-space:nowrap;
|
||||
padding-top:8px;
|
||||
padding-left:8px;
|
||||
display:block;
|
||||
float:left;
|
||||
background-image:url(resources/titlebar.gif);
|
||||
height:18px;
|
||||
}
|
||||
.contentContainer ul.blockList li.blockList caption span.tableTab, .contentContainer ul.blockList li.blockList caption span.activeTableTab {
|
||||
padding-top:0px;
|
||||
padding-left:0px;
|
||||
background-image:none;
|
||||
float:none;
|
||||
display:inline;
|
||||
}
|
||||
.overviewSummary .tabEnd, .packageSummary .tabEnd, .contentContainer ul.blockList li.blockList .tabEnd, .summary .tabEnd, .classUseContainer .tabEnd, .constantValuesContainer .tabEnd {
|
||||
width:10px;
|
||||
background-image:url(resources/titlebar_end.gif);
|
||||
background-repeat:no-repeat;
|
||||
background-position:top right;
|
||||
position:relative;
|
||||
float:left;
|
||||
}
|
||||
.contentContainer ul.blockList li.blockList .activeTableTab .tabEnd {
|
||||
width:10px;
|
||||
margin-right:5px;
|
||||
background-image:url(resources/activetitlebar_end.gif);
|
||||
background-repeat:no-repeat;
|
||||
background-position:top right;
|
||||
position:relative;
|
||||
float:left;
|
||||
}
|
||||
.contentContainer ul.blockList li.blockList .tableTab .tabEnd {
|
||||
width:10px;
|
||||
margin-right:5px;
|
||||
background-image:url(resources/titlebar_end.gif);
|
||||
background-repeat:no-repeat;
|
||||
background-position:top right;
|
||||
position:relative;
|
||||
float:left;
|
||||
}
|
||||
ul.blockList ul.blockList li.blockList table {
|
||||
margin:0 0 12px 0px;
|
||||
width:100%;
|
||||
}
|
||||
.tableSubHeadingColor {
|
||||
background-color: #EEEEFF;
|
||||
}
|
||||
.altColor {
|
||||
background-color:#eeeeef;
|
||||
}
|
||||
.rowColor {
|
||||
background-color:#ffffff;
|
||||
}
|
||||
.overviewSummary td, .packageSummary td, .contentContainer ul.blockList li.blockList td, .summary td, .classUseContainer td, .constantValuesContainer td {
|
||||
text-align:left;
|
||||
padding:3px 3px 3px 7px;
|
||||
}
|
||||
th.colFirst, th.colLast, th.colOne, .constantValuesContainer th {
|
||||
background:#dee3e9;
|
||||
border-top:1px solid #9eadc0;
|
||||
border-bottom:1px solid #9eadc0;
|
||||
text-align:left;
|
||||
padding:3px 3px 3px 7px;
|
||||
}
|
||||
td.colOne a:link, td.colOne a:active, td.colOne a:visited, td.colOne a:hover, td.colFirst a:link, td.colFirst a:active, td.colFirst a:visited, td.colFirst a:hover, td.colLast a:link, td.colLast a:active, td.colLast a:visited, td.colLast a:hover, .constantValuesContainer td a:link, .constantValuesContainer td a:active, .constantValuesContainer td a:visited, .constantValuesContainer td a:hover {
|
||||
font-weight:bold;
|
||||
}
|
||||
td.colFirst, th.colFirst {
|
||||
border-left:1px solid #9eadc0;
|
||||
white-space:nowrap;
|
||||
}
|
||||
td.colLast, th.colLast {
|
||||
border-right:1px solid #9eadc0;
|
||||
}
|
||||
td.colOne, th.colOne {
|
||||
border-right:1px solid #9eadc0;
|
||||
border-left:1px solid #9eadc0;
|
||||
}
|
||||
table.overviewSummary {
|
||||
padding:0px;
|
||||
margin-left:0px;
|
||||
}
|
||||
table.overviewSummary td.colFirst, table.overviewSummary th.colFirst,
|
||||
table.overviewSummary td.colOne, table.overviewSummary th.colOne {
|
||||
width:25%;
|
||||
vertical-align:middle;
|
||||
}
|
||||
table.packageSummary td.colFirst, table.overviewSummary th.colFirst {
|
||||
width:25%;
|
||||
vertical-align:middle;
|
||||
}
|
||||
/*
|
||||
Content styles
|
||||
*/
|
||||
.description pre {
|
||||
margin-top:0;
|
||||
}
|
||||
.deprecatedContent {
|
||||
margin:0;
|
||||
padding:10px 0;
|
||||
}
|
||||
.docSummary {
|
||||
padding:0;
|
||||
}
|
||||
/*
|
||||
Formatting effect styles
|
||||
*/
|
||||
.sourceLineNo {
|
||||
color:green;
|
||||
padding:0 30px 0 0;
|
||||
}
|
||||
h1.hidden {
|
||||
visibility:hidden;
|
||||
overflow:hidden;
|
||||
font-size:.9em;
|
||||
}
|
||||
.block {
|
||||
display:block;
|
||||
margin:3px 0 0 0;
|
||||
}
|
||||
.strong {
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
Spring
|
||||
*/
|
||||
|
||||
pre.code {
|
||||
background-color: #F8F8F8;
|
||||
border: 1px solid #CCCCCC;
|
||||
border-radius: 3px 3px 3px 3px;
|
||||
overflow: auto;
|
||||
padding: 10px;
|
||||
margin: 4px 20px 2px 0px;
|
||||
}
|
||||
|
||||
pre.code code, pre.code code * {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
pre.code code, pre.code code * {
|
||||
padding: 0 !important;
|
||||
margin: 0 !important;
|
||||
}
|
||||
|
|
@ -6,11 +6,17 @@ http://www.springsource.org
|
|||
Changes in version 3.2.2 (2013-03-07)
|
||||
--------------------------------------
|
||||
|
||||
* official support for Hibernate 4.2
|
||||
* ConfigurationClassPostProcessor consistently uses ClassLoader, not loading core JDK annotations via ASM (SPR-10249)
|
||||
* ConfigurationClassPostProcessor allows for overriding of scoped-proxy bean definitions (SPR-10265)
|
||||
* added "maximumAutoGrowSize" property to SpelParserConfiguration (SPR-10229)
|
||||
* SQLErrorCodeSQLExceptionTranslator tries to find SQLException with actual error code among causes (SPR-10260)
|
||||
* DefaultMessageListenerContainer invokes specified ExceptionListener for recovery exceptions as well (SPR-10230)
|
||||
* DefaultMessageListenerContainer logs recovery failures at error level and exposes "isRecovering()" method (SPR-10230)
|
||||
* MediaType throws dedicated InvalidMediaTypeException instead of generic IllegalArgumentException (SPR-10226)
|
||||
* marked spring-web module as 'distributable' in order for session replication to work on Tomcat (SPR-10219)
|
||||
* consistent use of LinkedHashMaps and independent getAttributeNames Enumeration in Servlet/Portlet mocks (SPR-10224)
|
||||
* support 'unless' expression for cache veto (SPR-8871)
|
||||
|
||||
|
||||
Changes in version 3.2.1 (2013-01-24)
|
||||
|
|
@ -34,7 +40,7 @@ Changes in version 3.2.1 (2013-01-24)
|
|||
* MessageSourceResourceBundle overrides JDK 1.6 containsKey method, avoiding NPE in getKeys (SPR-10136)
|
||||
* SpringValidationAdapter properly detects invalid value for JSR-303 field-level bean constraints (SPR-9332)
|
||||
* SpringBeanAutowiringInterceptor eagerly releases BeanFactory if post-construction fails (SPR-10013)
|
||||
* added "exposeAccessContext" flag JndiRmiClientInterceptor/ProxyFactoryBean (for WebLogic; SPR-9428)
|
||||
* added "exposeAccessContext" flag to JndiRmiClientInterceptor/ProxyFactoryBean (for WebLogic; SPR-9428)
|
||||
* MBeanExporter does not log warnings for manually unregistered MBeans (SPR-9451)
|
||||
* MBeanInfoAssembler impls expose actual method parameter names if possible (SPR-9985)
|
||||
* AbstractCacheManager accepts no caches defined, allowing for EHCache default cache setup (SPR-7955)
|
||||
|
|
|
|||
|
|
@ -152,6 +152,13 @@ public Book findBook(ISBN isbn, boolean checkWarehouse, boolean includeUsed)</pr
|
|||
|
||||
<programlisting language="java"><![CDATA[@Cacheable(value="book", condition="#name.length < 32")
|
||||
public Book findBook(String name)]]></programlisting>
|
||||
|
||||
<para>In addition the <literal>conditional</literal> parameter, the <literal>unless</literal> parameter can be used to veto the adding of a value to the cache. Unlike
|
||||
<literal>conditional</literal>, <literal>unless</literal> <literal>SpEL</literal> expressions are evalulated <emphasis>after</emphasis> the method has been called. Expanding
|
||||
on the previous example - perhaps we only want to cache paperback books:</para>
|
||||
|
||||
<programlisting language="java"><![CDATA[@Cacheable(value="book", condition="#name.length < 32", unless="#result.hardback")
|
||||
public Book findBook(String name)]]></programlisting>
|
||||
</section>
|
||||
|
||||
<section xml:id="cache-spel-context">
|
||||
|
|
@ -218,6 +225,13 @@ public Book findBook(String name)]]></programlisting>
|
|||
<emphasis><![CDATA[#arg]]></emphasis> stands for the argument index (starting from 0).</entry>
|
||||
<entry><screen>iban</screen> or <screen>a0</screen> (one can also use <screen>p0</screen> or <literal><![CDATA[p<#arg>]]></literal> notation as an alias).</entry>
|
||||
</row>
|
||||
<row>
|
||||
<entry>result</entry>
|
||||
<entry>evaluation context</entry>
|
||||
<entry>The result of the method call (the value to be cached). Only available in '<literal>unless</literal>' expressions and '<literal>cache evict</literal>'
|
||||
expression (when <literal>beforeInvocation</literal> is <literal>false</literal>).</entry>
|
||||
<entry><screen>#result</screen></entry>
|
||||
</row>
|
||||
</tbody>
|
||||
</tgroup>
|
||||
</table>
|
||||
|
|
|
|||
|
|
@ -840,9 +840,9 @@ public class ExampleJob extends QuartzJobBean {
|
|||
object. Of course, we still need to schedule the jobs themselves. This
|
||||
is done using triggers and a
|
||||
<classname>SchedulerFactoryBean</classname>. Several triggers are
|
||||
available within Quartz. Spring offers two subclassed triggers with
|
||||
convenient defaults: <classname>CronTriggerBean</classname> and
|
||||
<classname>SimpleTriggerBean</classname>.</para>
|
||||
available within Quartz and Spring offers two Quartz <interfacename>FactoryBean</interfacename>
|
||||
implementations with convenient defaults: <classname>CronTriggerFactoryBean</classname> and
|
||||
<classname>SimpleTriggerFactoryBean</classname>.</para>
|
||||
|
||||
<para>Triggers need to be scheduled. Spring offers a
|
||||
<classname>SchedulerFactoryBean</classname> that exposes triggers to be
|
||||
|
|
@ -851,7 +851,7 @@ public class ExampleJob extends QuartzJobBean {
|
|||
|
||||
<para>Find below a couple of examples:</para>
|
||||
|
||||
<programlisting language="xml"><bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerBean">
|
||||
<programlisting language="xml"><bean id="simpleTrigger" class="org.springframework.scheduling.quartz.SimpleTriggerFactoryBean">
|
||||
<!-- see the example of method invoking job above -->
|
||||
<property name="jobDetail" ref="jobDetail" />
|
||||
<!-- 10 seconds -->
|
||||
|
|
@ -860,7 +860,7 @@ public class ExampleJob extends QuartzJobBean {
|
|||
<property name="repeatInterval" value="50000" />
|
||||
</bean>
|
||||
|
||||
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
|
||||
<bean id="cronTrigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean">
|
||||
<property name="jobDetail" ref="exampleJob" />
|
||||
<!-- run every morning at 6 AM -->
|
||||
<property name="cronExpression" value="0 0 6 * * ?" />
|
||||
|
|
|
|||
Loading…
Reference in New Issue