diff options
Diffstat (limited to 'spring-expression/src/main')
12 files changed, 176 insertions, 126 deletions
diff --git a/spring-expression/src/main/java/org/springframework/expression/BeanResolver.java b/spring-expression/src/main/java/org/springframework/expression/BeanResolver.java index c95501ba..1f6726f1 100644 --- a/spring-expression/src/main/java/org/springframework/expression/BeanResolver.java +++ b/spring-expression/src/main/java/org/springframework/expression/BeanResolver.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -18,7 +18,9 @@ package org.springframework.expression; /** * A bean resolver can be registered with the evaluation context - * and will kick in for {@code @myBeanName} still expressions. + * and will kick in for {@code @myBeanName} and {@code &myBeanName} expressions. + * The <tt>&</tt> variant syntax allows access to the factory bean where + * relevant. * * @author Andy Clement * @since 3.0.3 @@ -26,7 +28,8 @@ package org.springframework.expression; public interface BeanResolver { /** - * Look up the named bean and return it. + * Look up the named bean and return it. If attempting to access a factory + * bean the name will have a <tt>&</tt> prefix. * @param context the current evaluation context * @param beanName the name of the bean to lookup * @return an object representing the bean diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java b/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java index 2d20e195..decb2f1c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/CodeFlow.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -148,6 +148,72 @@ public class CodeFlow implements Opcodes { } /** + * Called after the main expression evaluation method has been generated, this + * method will callback any registered FieldAdders or ClinitAdders to add any + * extra information to the class representing the compiled expression. + */ + public void finish() { + if (this.fieldAdders != null) { + for (FieldAdder fieldAdder : this.fieldAdders) { + fieldAdder.generateField(cw,this); + } + } + if (this.clinitAdders != null) { + MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "<clinit>", "()V", null, null); + mv.visitCode(); + this.nextFreeVariableId = 0; // To 0 because there is no 'this' in a clinit + for (ClinitAdder clinitAdder : this.clinitAdders) { + clinitAdder.generateCode(mv, this); + } + mv.visitInsn(RETURN); + mv.visitMaxs(0,0); // not supplied due to COMPUTE_MAXS + mv.visitEnd(); + } + } + + /** + * Register a FieldAdder which will add a new field to the generated + * class to support the code produced by an ast nodes primary + * generateCode() method. + */ + public void registerNewField(FieldAdder fieldAdder) { + if (this.fieldAdders == null) { + this.fieldAdders = new ArrayList<FieldAdder>(); + } + this.fieldAdders.add(fieldAdder); + } + + /** + * Register a ClinitAdder which will add code to the static + * initializer in the generated class to support the code + * produced by an ast nodes primary generateCode() method. + */ + public void registerNewClinit(ClinitAdder clinitAdder) { + if (this.clinitAdders == null) { + this.clinitAdders = new ArrayList<ClinitAdder>(); + } + this.clinitAdders.add(clinitAdder); + } + + public int nextFieldId() { + return this.nextFieldId++; + } + + public int nextFreeVariableId() { + return this.nextFreeVariableId++; + } + + public String getClassName() { + return this.clazzName; + } + + @Deprecated + public String getClassname() { + return this.clazzName; + } + + + /** * Insert any necessary cast and value call to convert from a boxed type to a * primitive value * @param mv the method visitor into which instructions should be inserted @@ -779,74 +845,6 @@ public class CodeFlow implements Opcodes { } /** - * Called after the main expression evaluation method has been generated, this - * method will callback any registered FieldAdders or ClinitAdders to add any - * extra information to the class representing the compiled expression. - */ - public void finish() { - if (fieldAdders != null) { - for (FieldAdder fieldAdder: fieldAdders) { - fieldAdder.generateField(cw,this); - } - } - if (clinitAdders != null) { - MethodVisitor mv = cw.visitMethod(ACC_PUBLIC | ACC_STATIC, "<clinit>", "()V", null, null); - mv.visitCode(); - nextFreeVariableId = 0; // To 0 because there is no 'this' in a clinit - for (ClinitAdder clinitAdder: clinitAdders) { - clinitAdder.generateCode(mv, this); - } - mv.visitInsn(RETURN); - mv.visitMaxs(0,0); // not supplied due to COMPUTE_MAXS - mv.visitEnd(); - } - } - - /** - * Register a FieldAdder which will add a new field to the generated - * class to support the code produced by an ast nodes primary - * generateCode() method. - */ - public void registerNewField(FieldAdder fieldAdder) { - if (fieldAdders == null) { - fieldAdders = new ArrayList<FieldAdder>(); - } - fieldAdders.add(fieldAdder); - } - - /** - * Register a ClinitAdder which will add code to the static - * initializer in the generated class to support the code - * produced by an ast nodes primary generateCode() method. - */ - public void registerNewClinit(ClinitAdder clinitAdder) { - if (clinitAdders == null) { - clinitAdders = new ArrayList<ClinitAdder>(); - } - clinitAdders.add(clinitAdder); - } - - public int nextFieldId() { - return nextFieldId++; - } - - public int nextFreeVariableId() { - return nextFreeVariableId++; - } - - public String getClassname() { - return clazzName; - } - - public interface FieldAdder { - public void generateField(ClassWriter cw, CodeFlow codeflow); - } - - public interface ClinitAdder { - public void generateCode(MethodVisitor mv, CodeFlow codeflow); - } - - /** * Create the optimal instruction for loading a number on the stack. * @param mv where to insert the bytecode * @param value the value to be loaded @@ -977,4 +975,15 @@ public class CodeFlow implements Opcodes { } + public interface FieldAdder { + + void generateField(ClassWriter cw, CodeFlow codeflow); + } + + + public interface ClinitAdder { + + void generateCode(MethodVisitor mv, CodeFlow codeflow); + } + } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java index 1431554a..40c7054c 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/SpelMessage.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -214,7 +214,7 @@ public enum SpelMessage { "A problem occurred when trying to resolve bean ''{0}'':''{1}''"), INVALID_BEAN_REFERENCE(Kind.ERROR, 1059, - "@ can only be followed by an identifier or a quoted name"), + "@ or & can only be followed by an identifier or a quoted name"), TYPE_NAME_EXPECTED_FOR_ARRAY_CONSTRUCTION(Kind.ERROR, 1060, "Expected the type of the new array to be specified as a String but found ''{0}''"), diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/BeanReference.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/BeanReference.java index 5f0c8404..67021482 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/BeanReference.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/BeanReference.java @@ -25,12 +25,15 @@ import org.springframework.expression.spel.SpelEvaluationException; import org.springframework.expression.spel.SpelMessage; /** - * Represents a bean reference to a type, for example "@foo" or "@'foo.bar'" - * + * Represents a bean reference to a type, for example <tt>@foo</tt> or <tt>@'foo.bar'</tt>. + * For a FactoryBean the syntax <tt>&foo</tt> can be used to access the factory itself. + * * @author Andy Clement */ public class BeanReference extends SpelNodeImpl { + private final static String FACTORY_BEAN_PREFIX = "&"; + private final String beanName; @@ -59,7 +62,10 @@ public class BeanReference extends SpelNodeImpl { @Override public String toStringAST() { - StringBuilder sb = new StringBuilder("@"); + StringBuilder sb = new StringBuilder(); + if (!this.beanName.startsWith(FACTORY_BEAN_PREFIX)) { + sb.append("@"); + } if (!this.beanName.contains(".")) { sb.append(this.beanName); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java index ae15a6f1..32a837ea 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/Indexer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -694,7 +694,7 @@ public class Indexer extends SpelNodeImpl { newElements--; } } - catch (Exception ex) { + catch (Throwable ex) { throw new SpelEvaluationException(getStartPosition(), ex, SpelMessage.UNABLE_TO_GROW_COLLECTION); } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java index b4134b63..1a435b86 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java @@ -132,8 +132,8 @@ public class InlineList extends SpelNodeImpl { @Override public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - final String constantFieldName = "inlineList$"+codeflow.nextFieldId(); - final String clazzname = codeflow.getClassname(); + final String constantFieldName = "inlineList$" + codeflow.nextFieldId(); + final String className = codeflow.getClassName(); codeflow.registerNewField(new CodeFlow.FieldAdder() { public void generateField(ClassWriter cw, CodeFlow codeflow) { @@ -143,11 +143,11 @@ public class InlineList extends SpelNodeImpl { codeflow.registerNewClinit(new CodeFlow.ClinitAdder() { public void generateCode(MethodVisitor mv, CodeFlow codeflow) { - generateClinitCode(clazzname,constantFieldName, mv,codeflow,false); + generateClinitCode(className, constantFieldName, mv, codeflow, false); } }); - mv.visitFieldInsn(GETSTATIC, clazzname, constantFieldName, "Ljava/util/List;"); + mv.visitFieldInsn(GETSTATIC, className, constantFieldName, "Ljava/util/List;"); codeflow.pushDescriptor("Ljava/util/List"); } @@ -158,8 +158,8 @@ public class InlineList extends SpelNodeImpl { if (!nested) { mv.visitFieldInsn(PUTSTATIC, clazzname, constantFieldName, "Ljava/util/List;"); } - int childcount = getChildCount(); - for (int c=0; c < childcount; c++) { + int childCount = getChildCount(); + for (int c = 0; c < childCount; c++) { if (!nested) { mv.visitFieldInsn(GETSTATIC, clazzname, constantFieldName, "Ljava/util/List;"); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java index 5a3063b6..6c880bdd 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/ast/OperatorInstanceof.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -53,8 +53,9 @@ public class OperatorInstanceof extends Operator { */ @Override public BooleanTypedValue getValueInternal(ExpressionState state) throws EvaluationException { + SpelNodeImpl rightOperand = getRightOperand(); TypedValue left = getLeftOperand().getValueInternal(state); - TypedValue right = getRightOperand().getValueInternal(state); + TypedValue right = rightOperand.getValueInternal(state); Object leftValue = left.getValue(); Object rightValue = right.getValue(); BooleanTypedValue result = null; @@ -71,7 +72,11 @@ public class OperatorInstanceof extends Operator { result = BooleanTypedValue.forValue(rightClass.isAssignableFrom(leftValue.getClass())); } this.type = rightClass; - this.exitTypeDescriptor = "Z"; + if (rightOperand instanceof TypeReference) { + // Can only generate bytecode where the right operand is a direct type reference, + // not if it is indirect (for example when right operand is a variable reference) + this.exitTypeDescriptor = "Z"; + } return result; } @@ -83,7 +88,16 @@ public class OperatorInstanceof extends Operator { @Override public void generateCode(MethodVisitor mv, CodeFlow cf) { getLeftOperand().generateCode(mv, cf); - mv.visitTypeInsn(INSTANCEOF,Type.getInternalName(this.type)); + CodeFlow.insertBoxIfNecessary(mv, cf.lastDescriptor()); + if (this.type.isPrimitive()) { + // always false - but left operand code always driven + // in case it had side effects + mv.visitInsn(POP); + mv.visitInsn(ICONST_0); // value of false + } + else { + mv.visitTypeInsn(INSTANCEOF,Type.getInternalName(this.type)); + } cf.pushDescriptor(this.exitTypeDescriptor); } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java index 8531aa0d..600ffda6 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/InternalSpelExpressionParser.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -525,7 +525,7 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { // parse: @beanname @'bean.name' // quoted if dotted private boolean maybeEatBeanReference() { - if (peekToken(TokenKind.BEAN_REF)) { + if (peekToken(TokenKind.BEAN_REF) || peekToken(TokenKind.FACTORY_BEAN_REF)) { Token beanRefToken = nextToken(); Token beanNameToken = null; String beanName = null; @@ -543,7 +543,14 @@ class InternalSpelExpressionParser extends TemplateAwareExpressionParser { SpelMessage.INVALID_BEAN_REFERENCE); } - BeanReference beanReference = new BeanReference(toPos(beanNameToken) ,beanName); + BeanReference beanReference = null; + if (beanRefToken.getKind() == TokenKind.FACTORY_BEAN_REF) { + String beanNameString = new StringBuilder().append(TokenKind.FACTORY_BEAN_REF.tokenChars).append(beanName).toString(); + beanReference = new BeanReference(toPos(beanRefToken.startPos,beanNameToken.endPos),beanNameString); + } + else { + beanReference = new BeanReference(toPos(beanNameToken) ,beanName); + } this.constructedNodes.push(beanReference); return true; } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java index 4a31941e..0b577554 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/TokenKind.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2014 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -112,6 +112,8 @@ enum TokenKind { BEAN_REF("@"), + FACTORY_BEAN_REF("&"), + SYMBOLIC_OR("||"), SYMBOLIC_AND("&&"), diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java index fb912228..e0408f10 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/standard/Tokenizer.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -182,12 +182,12 @@ class Tokenizer { } break; case '&': - if (!isTwoCharToken(TokenKind.SYMBOLIC_AND)) { - throw new InternalParseException(new SpelParseException( - this.expressionString, this.pos, SpelMessage.MISSING_CHARACTER, - "&")); + if (isTwoCharToken(TokenKind.SYMBOLIC_AND)) { + pushPairToken(TokenKind.SYMBOLIC_AND); + } + else { + pushCharToken(TokenKind.FACTORY_BEAN_REF); } - pushPairToken(TokenKind.SYMBOLIC_AND); break; case '|': if (!isTwoCharToken(TokenKind.SYMBOLIC_OR)) { diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java index 4a3a5cfe..8cb54e8f 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/ReflectivePropertyAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -33,7 +33,6 @@ import org.springframework.asm.MethodVisitor; import org.springframework.core.MethodParameter; import org.springframework.core.convert.Property; import org.springframework.core.convert.TypeDescriptor; -import org.springframework.core.style.ToStringCreator; import org.springframework.expression.AccessException; import org.springframework.expression.EvaluationContext; import org.springframework.expression.EvaluationException; @@ -70,11 +69,14 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { } - private final Map<CacheKey, InvokerPair> readerCache = new ConcurrentHashMap<CacheKey, InvokerPair>(64); + private final Map<PropertyCacheKey, InvokerPair> readerCache = + new ConcurrentHashMap<PropertyCacheKey, InvokerPair>(64); - private final Map<CacheKey, Member> writerCache = new ConcurrentHashMap<CacheKey, Member>(64); + private final Map<PropertyCacheKey, Member> writerCache = + new ConcurrentHashMap<PropertyCacheKey, Member>(64); - private final Map<CacheKey, TypeDescriptor> typeDescriptorCache = new ConcurrentHashMap<CacheKey, TypeDescriptor>(64); + private final Map<PropertyCacheKey, TypeDescriptor> typeDescriptorCache = + new ConcurrentHashMap<PropertyCacheKey, TypeDescriptor>(64); private InvokerPair lastReadInvokerPair; @@ -96,7 +98,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { if (type.isArray() && name.equals("length")) { return true; } - CacheKey cacheKey = new CacheKey(type, name, target instanceof Class); + PropertyCacheKey cacheKey = new PropertyCacheKey(type, name, target instanceof Class); if (this.readerCache.containsKey(cacheKey)) { return true; } @@ -140,7 +142,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { return new TypedValue(Array.getLength(target)); } - CacheKey cacheKey = new CacheKey(type, name, target instanceof Class); + PropertyCacheKey cacheKey = new PropertyCacheKey(type, name, target instanceof Class); InvokerPair invoker = this.readerCache.get(cacheKey); lastReadInvokerPair = invoker; @@ -202,7 +204,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { return false; } Class<?> type = (target instanceof Class ? (Class<?>) target : target.getClass()); - CacheKey cacheKey = new CacheKey(type, name, target instanceof Class); + PropertyCacheKey cacheKey = new PropertyCacheKey(type, name, target instanceof Class); if (this.writerCache.containsKey(cacheKey)) { return true; } @@ -244,7 +246,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { throw new AccessException("Type conversion failure", evaluationException); } } - CacheKey cacheKey = new CacheKey(type, name, target instanceof Class); + PropertyCacheKey cacheKey = new PropertyCacheKey(type, name, target instanceof Class); Member cachedMember = this.writerCache.get(cacheKey); if (cachedMember == null || cachedMember instanceof Method) { @@ -301,7 +303,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { if (type.isArray() && name.equals("length")) { return TypeDescriptor.valueOf(Integer.TYPE); } - CacheKey cacheKey = new CacheKey(type, name, target instanceof Class); + PropertyCacheKey cacheKey = new PropertyCacheKey(type, name, target instanceof Class); TypeDescriptor typeDescriptor = this.typeDescriptorCache.get(cacheKey); if (typeDescriptor == null) { // attempt to populate the cache entry @@ -466,7 +468,7 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { return this; } - CacheKey cacheKey = new CacheKey(type, name, target instanceof Class); + PropertyCacheKey cacheKey = new PropertyCacheKey(type, name, target instanceof Class); InvokerPair invocationTarget = this.readerCache.get(cacheKey); if (invocationTarget == null || invocationTarget.member instanceof Method) { @@ -520,17 +522,17 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { } - private static class CacheKey { + private static final class PropertyCacheKey implements Comparable<PropertyCacheKey> { private final Class<?> clazz; - private final String name; + private final String property; private boolean targetIsClass; - public CacheKey(Class<?> clazz, String name, boolean targetIsClass) { + public PropertyCacheKey(Class<?> clazz, String name, boolean targetIsClass) { this.clazz = clazz; - this.name = name; + this.property = name; this.targetIsClass = targetIsClass; } @@ -539,23 +541,32 @@ public class ReflectivePropertyAccessor implements PropertyAccessor { if (this == other) { return true; } - if (!(other instanceof CacheKey)) { + if (!(other instanceof PropertyCacheKey)) { return false; } - CacheKey otherKey = (CacheKey) other; - return (this.clazz.equals(otherKey.clazz) && this.name.equals(otherKey.name) && + PropertyCacheKey otherKey = (PropertyCacheKey) other; + return (this.clazz == otherKey.clazz && this.property.equals(otherKey.property) && this.targetIsClass == otherKey.targetIsClass); } @Override public int hashCode() { - return (this.clazz.hashCode() * 29 + this.name.hashCode()); + return (this.clazz.hashCode() * 29 + this.property.hashCode()); } @Override public String toString() { - return new ToStringCreator(this).append("clazz", this.clazz).append("name", - this.name).append("targetIsClass", this.targetIsClass).toString(); + return "CacheKey [clazz=" + this.clazz.getName() + ", property=" + this.property + ", " + + this.property + ", targetIsClass=" + this.targetIsClass + "]"; + } + + @Override + public int compareTo(PropertyCacheKey other) { + int result = this.clazz.getName().compareTo(other.clazz.getName()); + if (result == 0) { + result = this.property.compareTo(other.property); + } + return result; } } diff --git a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java index 690a7e57..253e4bb3 100644 --- a/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java +++ b/spring-expression/src/main/java/org/springframework/expression/spel/support/StandardTypeConverter.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2016 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -36,7 +36,7 @@ import org.springframework.util.Assert; */ public class StandardTypeConverter implements TypeConverter { - private static ConversionService defaultConversionService; + private static volatile ConversionService defaultConversionService; private final ConversionService conversionService; @@ -45,10 +45,8 @@ public class StandardTypeConverter implements TypeConverter { * Create a StandardTypeConverter for the default ConversionService. */ public StandardTypeConverter() { - synchronized (this) { - if (defaultConversionService == null) { - defaultConversionService = new DefaultConversionService(); - } + if (defaultConversionService == null) { + defaultConversionService = new DefaultConversionService(); } this.conversionService = defaultConversionService; } |