Skip to content

Commit bad74dc

Browse files
committed
Revision of SpelCompiler support, resolving a subpackage cycle through moving CodeFlow and CompilablePropertyAccessor to the main spel package
Also contains explicit ClassLoader management, passed through StandardBeanExpressionResolver and SpelParserConfiguration to SpelCompiler lookup. Issue: SPR-10943
1 parent 8fb5927 commit bad74dc

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

47 files changed

+580
-491
lines changed

spring-context/src/main/java/org/springframework/context/expression/StandardBeanExpressionResolver.java

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -27,6 +27,7 @@
2727
import org.springframework.expression.Expression;
2828
import org.springframework.expression.ExpressionParser;
2929
import org.springframework.expression.ParserContext;
30+
import org.springframework.expression.spel.SpelParserConfiguration;
3031
import org.springframework.expression.spel.standard.SpelExpressionParser;
3132
import org.springframework.expression.spel.support.StandardEvaluationContext;
3233
import org.springframework.expression.spel.support.StandardTypeConverter;
@@ -58,7 +59,7 @@ public class StandardBeanExpressionResolver implements BeanExpressionResolver {
5859

5960
private String expressionSuffix = DEFAULT_EXPRESSION_SUFFIX;
6061

61-
private ExpressionParser expressionParser = new SpelExpressionParser();
62+
private ExpressionParser expressionParser;
6263

6364
private final Map<String, Expression> expressionCache = new ConcurrentHashMap<String, Expression>(256);
6465

@@ -81,6 +82,23 @@ public String getExpressionSuffix() {
8182
};
8283

8384

85+
/**
86+
* Create a new {@code StandardBeanExpressionResolver} with default settings.
87+
*/
88+
public StandardBeanExpressionResolver() {
89+
this.expressionParser = new SpelExpressionParser();
90+
}
91+
92+
/**
93+
* Create a new {@code StandardBeanExpressionResolver} with the given bean class loader,
94+
* using it as the basis for expression compilation.
95+
* @param beanClassLoader the factory's bean class loader
96+
*/
97+
public StandardBeanExpressionResolver(ClassLoader beanClassLoader) {
98+
this.expressionParser = new SpelExpressionParser(new SpelParserConfiguration(null, beanClassLoader));
99+
}
100+
101+
84102
/**
85103
* Set the prefix that an expression string starts with.
86104
* The default is "#{".

spring-context/src/main/java/org/springframework/context/support/AbstractApplicationContext.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -548,7 +548,7 @@ protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
548548
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
549549
// Tell the internal bean factory to use the context's class loader etc.
550550
beanFactory.setBeanClassLoader(getClassLoader());
551-
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver());
551+
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
552552
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
553553

554554
// Configure the bean factory with context callbacks.
Lines changed: 61 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
/*
2-
* Copyright 2014 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
66
* You may obtain a copy of the License at
77
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
8+
* http://www.apache.org/licenses/LICENSE-2.0
99
*
1010
* Unless required by applicable law or agreed to in writing, software
1111
* distributed under the License is distributed on an "AS IS" BASIS,
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
package org.springframework.expression.spel.standard;
17+
package org.springframework.expression.spel;
1818

1919
import java.lang.reflect.Constructor;
2020
import java.lang.reflect.Method;
@@ -40,13 +40,15 @@ public class CodeFlow implements Opcodes {
4040
* sub-expressions like the expressions for the argument values in a method invocation
4141
* expression.
4242
*/
43-
private Stack<ArrayList<String>> compilationScopes;
43+
private final Stack<ArrayList<String>> compilationScopes;
44+
4445

4546
public CodeFlow() {
46-
compilationScopes = new Stack<ArrayList<String>>();
47-
compilationScopes.add(new ArrayList<String>());
47+
this.compilationScopes = new Stack<ArrayList<String>>();
48+
this.compilationScopes.add(new ArrayList<String>());
4849
}
4950

51+
5052
/**
5153
* Push the byte code to load the target (i.e. what was passed as the first argument
5254
* to CompiledExpression.getValue(target, context))
@@ -62,7 +64,7 @@ public void loadTarget(MethodVisitor mv) {
6264
*/
6365
public void pushDescriptor(String descriptor) {
6466
Assert.notNull(descriptor);
65-
compilationScopes.peek().add(descriptor);
67+
this.compilationScopes.peek().add(descriptor);
6668
}
6769

6870
/**
@@ -71,7 +73,7 @@ public void pushDescriptor(String descriptor) {
7173
* each argument will be evaluated in a new scope.
7274
*/
7375
public void enterCompilationScope() {
74-
compilationScopes.push(new ArrayList<String>());
76+
this.compilationScopes.push(new ArrayList<String>());
7577
}
7678

7779
/**
@@ -80,18 +82,17 @@ public void enterCompilationScope() {
8082
* returns us to the previous (outer) scope.
8183
*/
8284
public void exitCompilationScope() {
83-
compilationScopes.pop();
85+
this.compilationScopes.pop();
8486
}
8587

8688
/**
87-
* @return the descriptor for the item currently on top of the stack (in the current
88-
* scope)
89+
* @return the descriptor for the item currently on top of the stack (in the current scope)
8990
*/
9091
public String lastDescriptor() {
91-
if (compilationScopes.peek().size()==0) {
92+
if (this.compilationScopes.peek().size() == 0) {
9293
return null;
9394
}
94-
return compilationScopes.peek().get(compilationScopes.peek().size()-1);
95+
return this.compilationScopes.peek().get(this.compilationScopes.peek().size() - 1);
9596
}
9697

9798
/**
@@ -105,13 +106,14 @@ public void unboxBooleanIfNecessary(MethodVisitor mv) {
105106
}
106107
}
107108

109+
108110
/**
109111
* Insert any necessary cast and value call to convert from a boxed type to a
110112
* primitive value
111113
* @param mv the method visitor into which instructions should be inserted
112114
* @param ch the primitive type desired as output
113-
* @param isObject indicates whether the type on the stack is being thought of as
114-
* Object (and so requires a cast)
115+
* @param isObject indicates whether the type on the stack is being thought of
116+
* as Object (and so requires a cast)
115117
*/
116118
public static void insertUnboxInsns(MethodVisitor mv, char ch, boolean isObject) {
117119
switch (ch) {
@@ -179,14 +181,14 @@ public static void insertUnboxInsns(MethodVisitor mv, char ch, boolean isObject)
179181
*/
180182
public static String createSignatureDescriptor(Method method) {
181183
Class<?>[] params = method.getParameterTypes();
182-
StringBuilder s = new StringBuilder();
183-
s.append("(");
184-
for (int i = 0, max = params.length; i < max; i++) {
185-
s.append(toJVMDescriptor(params[i]));
184+
StringBuilder sb = new StringBuilder();
185+
sb.append("(");
186+
for (Class<?> param : params) {
187+
sb.append(toJVMDescriptor(param));
186188
}
187-
s.append(")");
188-
s.append(toJVMDescriptor(method.getReturnType()));
189-
return s.toString();
189+
sb.append(")");
190+
sb.append(toJVMDescriptor(method.getReturnType()));
191+
return sb.toString();
190192
}
191193

192194
/**
@@ -199,21 +201,20 @@ public static String createSignatureDescriptor(Method method) {
199201
*/
200202
public static String createSignatureDescriptor(Constructor<?> ctor) {
201203
Class<?>[] params = ctor.getParameterTypes();
202-
StringBuilder s = new StringBuilder();
203-
s.append("(");
204-
for (int i = 0, max = params.length; i < max; i++) {
205-
s.append(toJVMDescriptor(params[i]));
204+
StringBuilder sb = new StringBuilder();
205+
sb.append("(");
206+
for (Class<?> param : params) {
207+
sb.append(toJVMDescriptor(param));
206208
}
207-
s.append(")V");
208-
return s.toString();
209+
sb.append(")V");
210+
return sb.toString();
209211
}
210212

211213
/**
212214
* Determine the JVM descriptor for a specified class. Unlike the other descriptors
213215
* used in the compilation process, this is the one the JVM wants, so this one
214216
* includes any necessary trailing semicolon (e.g. Ljava/lang/String; rather than
215217
* Ljava/lang/String)
216-
*
217218
* @param clazz a class
218219
* @return the JVM descriptor for the class
219220
*/
@@ -262,39 +263,39 @@ else if (clazz == Short.TYPE) {
262263
}
263264

264265
/**
265-
* Determine the descriptor for an object instance (or null).
266-
* @param value an object (possibly null)
267-
* @return the type descriptor for the object (descriptor is "Ljava/lang/Object" for
268-
* null value)
266+
* Determine the descriptor for an object instance (or {@code null}).
267+
* @param value an object (possibly {@code null})
268+
* @return the type descriptor for the object
269+
* (descriptor is "Ljava/lang/Object" for {@code null} value)
269270
*/
270271
public static String toDescriptorFromObject(Object value) {
271272
if (value == null) {
272273
return "Ljava/lang/Object";
273-
} else {
274+
}
275+
else {
274276
return toDescriptor(value.getClass());
275277
}
276278
}
277279

278280
/**
279281
* @param descriptor type descriptor
280-
* @return true if the descriptor is for a boolean primitive or boolean reference type
282+
* @return {@code true} if the descriptor is for a boolean primitive or boolean reference type
281283
*/
282284
public static boolean isBooleanCompatible(String descriptor) {
283-
return descriptor != null
284-
&& (descriptor.equals("Z") || descriptor.equals("Ljava/lang/Boolean"));
285+
return (descriptor != null && (descriptor.equals("Z") || descriptor.equals("Ljava/lang/Boolean")));
285286
}
286287

287288
/**
288289
* @param descriptor type descriptor
289-
* @return true if the descriptor is for a primitive type
290+
* @return {@code true} if the descriptor is for a primitive type
290291
*/
291292
public static boolean isPrimitive(String descriptor) {
292-
return descriptor!=null && descriptor.length()==1;
293+
return (descriptor != null && descriptor.length() == 1);
293294
}
294295

295296
/**
296297
* @param descriptor the descriptor for a possible primitive array
297-
* @return true if the descriptor is for a primitive array (e.g. "[[I")
298+
* @return {@code true} if the descriptor is for a primitive array (e.g. "[[I")
298299
*/
299300
public static boolean isPrimitiveArray(String descriptor) {
300301
boolean primitive = true;
@@ -312,14 +313,13 @@ public static boolean isPrimitiveArray(String descriptor) {
312313
/**
313314
* Determine if boxing/unboxing can get from one type to the other. Assumes at least
314315
* one of the types is in boxed form (i.e. single char descriptor).
315-
*
316-
* @return true if it is possible to get (via boxing) from one descriptor to the other
316+
* @return {@code true} if it is possible to get (via boxing) from one descriptor to the other
317317
*/
318318
public static boolean areBoxingCompatible(String desc1, String desc2) {
319319
if (desc1.equals(desc2)) {
320320
return true;
321321
}
322-
if (desc1.length()==1) {
322+
if (desc1.length() == 1) {
323323
if (desc1.equals("D")) {
324324
return desc2.equals("Ljava/lang/Double");
325325
}
@@ -336,7 +336,7 @@ else if (desc1.equals("Z")) {
336336
return desc2.equals("Ljava/lang/Boolean");
337337
}
338338
}
339-
else if (desc2.length()==1) {
339+
else if (desc2.length() == 1) {
340340
if (desc2.equals("D")) {
341341
return desc1.equals("Ljava/lang/Double");
342342
}
@@ -361,14 +361,14 @@ else if (desc2.equals("Z")) {
361361
* compilation process only (currently) supports certain number types. These are
362362
* double, float, long and int.
363363
* @param descriptor the descriptor for a type
364-
* @return true if the descriptor is for a supported numeric type or boolean
364+
* @return {@code true} if the descriptor is for a supported numeric type or boolean
365365
*/
366366
public static boolean isPrimitiveOrUnboxableSupportedNumberOrBoolean(String descriptor) {
367-
if (descriptor==null) {
367+
if (descriptor == null) {
368368
return false;
369369
}
370-
if (descriptor.length()==1) {
371-
return "DFJZI".indexOf(descriptor.charAt(0))!=-1;
370+
if (descriptor.length( )== 1) {
371+
return ("DFJZI".indexOf(descriptor.charAt(0)) != -1);
372372
}
373373
if (descriptor.startsWith("Ljava/lang/")) {
374374
if (descriptor.equals("Ljava/lang/Double") || descriptor.equals("Ljava/lang/Integer") ||
@@ -385,14 +385,14 @@ public static boolean isPrimitiveOrUnboxableSupportedNumberOrBoolean(String desc
385385
* process only (currently) supports certain number types. These are double, float,
386386
* long and int.
387387
* @param descriptor the descriptor for a type
388-
* @return true if the descriptor is for a supported numeric type
388+
* @return {@code true} if the descriptor is for a supported numeric type
389389
*/
390390
public static boolean isPrimitiveOrUnboxableSupportedNumber(String descriptor) {
391-
if (descriptor==null) {
391+
if (descriptor == null) {
392392
return false;
393393
}
394-
if (descriptor.length()==1) {
395-
return "DFJI".indexOf(descriptor.charAt(0))!=-1;
394+
if (descriptor.length() == 1) {
395+
return ("DFJI".indexOf(descriptor.charAt(0)) != -1);
396396
}
397397
if (descriptor.startsWith("Ljava/lang/")) {
398398
if (descriptor.equals("Ljava/lang/Double") || descriptor.equals("Ljava/lang/Integer") ||
@@ -404,12 +404,11 @@ public static boolean isPrimitiveOrUnboxableSupportedNumber(String descriptor) {
404404
}
405405

406406
/**
407-
* @param descriptor a descriptor for a type that should have a primitive
408-
* representation
407+
* @param descriptor a descriptor for a type that should have a primitive representation
409408
* @return the single character descriptor for a primitive input descriptor
410409
*/
411410
public static char toPrimitiveTargetDesc(String descriptor) {
412-
if (descriptor.length()==1) {
411+
if (descriptor.length() == 1) {
413412
return descriptor.charAt(0);
414413
}
415414
if (descriptor.equals("Ljava/lang/Double")) {
@@ -438,13 +437,13 @@ else if (descriptor.equals("Ljava/lang/Boolean")) {
438437
* @param descriptor the descriptor of the type to cast to
439438
*/
440439
public static void insertCheckCast(MethodVisitor mv, String descriptor) {
441-
if (descriptor.length()!=1) {
442-
if (descriptor.charAt(0)=='[') {
443-
if (CodeFlow.isPrimitiveArray(descriptor)) {
440+
if (descriptor.length() != 1) {
441+
if (descriptor.charAt(0) == '[') {
442+
if (isPrimitiveArray(descriptor)) {
444443
mv.visitTypeInsn(CHECKCAST, descriptor);
445444
}
446445
else {
447-
mv.visitTypeInsn(CHECKCAST, descriptor+";");
446+
mv.visitTypeInsn(CHECKCAST, descriptor + ";");
448447
}
449448
}
450449
else {
@@ -557,14 +556,14 @@ else if (name.equals("short")) {
557556
}
558557
else {
559558
if (name.charAt(0) != '[') {
560-
return new StringBuilder("L").append(type.getName().replace('.', '/')).toString();
559+
return "L" + type.getName().replace('.', '/');
561560
}
562561
else {
563562
if (name.endsWith(";")) {
564563
return name.substring(0, name.length() - 1).replace('.', '/');
565564
}
566565
else {
567-
return name; // array has primitive component type
566+
return name; // array has primitive component type
568567
}
569568
}
570569
}
@@ -595,7 +594,7 @@ private static String[] toDescriptors(Class<?>[] types) {
595594
int typesCount = types.length;
596595
String[] descriptors = new String[typesCount];
597596
for (int p = 0; p < typesCount; p++) {
598-
descriptors[p] = CodeFlow.toDescriptor(types[p]);
597+
descriptors[p] = toDescriptor(types[p]);
599598
}
600599
return descriptors;
601600
}

0 commit comments

Comments
 (0)