Browse Source

Optimize InlineMap and InlineList in SpEL

- Make InlineList#constant and InlineMap#constant final.

- Add more assertions for InlineMap tests.

Closes gh-30251
pull/31075/merge
Harry Yang 3 years ago committed by Sam Brannen
parent
commit
0e0c298dcf
  1. 44
      spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java
  2. 75
      spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineMap.java
  3. 3
      spring-expression/src/test/java/org/springframework/expression/spel/MapTests.java

44
spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineList.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2021 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -35,55 +35,57 @@ import org.springframework.util.Assert;
* *
* @author Andy Clement * @author Andy Clement
* @author Sam Brannen * @author Sam Brannen
* @author Harry Yang
* @since 3.0.4 * @since 3.0.4
*/ */
public class InlineList extends SpelNodeImpl { public class InlineList extends SpelNodeImpl {
// If the list is purely literals, it is a constant value and can be computed and cached // If the list is purely literals, it is a constant value and can be computed and cached
@Nullable @Nullable
private TypedValue constant; // TODO must be immutable list private final TypedValue constant;
public InlineList(int startPos, int endPos, SpelNodeImpl... args) { public InlineList(int startPos, int endPos, SpelNodeImpl... args) {
super(startPos, endPos, args); super(startPos, endPos, args);
checkIfConstant(); this.constant = computeConstantValue();
} }
/** /**
* If all the components of the list are constants, or lists that themselves contain constants, then a constant list * If all the components of the list are constants, or lists
* can be built to represent this node. This will speed up later getValue calls and reduce the amount of garbage * that themselves contain constants, then a constant list
* can be built to represent this node. This will speed up
* later getValue calls and reduce the amount of garbage
* created. * created.
*/ */
private void checkIfConstant() { @Nullable
boolean isConstant = true; private TypedValue computeConstantValue() {
for (int c = 0, max = getChildCount(); c < max; c++) { for (int c = 0, max = getChildCount(); c < max; c++) {
SpelNode child = getChild(c); SpelNode child = getChild(c);
if (!(child instanceof Literal)) { if (!(child instanceof Literal)) {
if (child instanceof InlineList inlineList) { if (child instanceof InlineList inlineList) {
if (!inlineList.isConstant()) { if (!inlineList.isConstant()) {
isConstant = false; return null;
} }
} }
else { else {
isConstant = false; return null;
} }
} }
} }
if (isConstant) {
List<Object> constantList = new ArrayList<>(); List<Object> constantList = new ArrayList<>();
int childcount = getChildCount(); int childcount = getChildCount();
for (int c = 0; c < childcount; c++) { for (int c = 0; c < childcount; c++) {
SpelNode child = getChild(c); SpelNode child = getChild(c);
if (child instanceof Literal literal) { if (child instanceof Literal literal) {
constantList.add(literal.getLiteralValue().getValue()); constantList.add(literal.getLiteralValue().getValue());
} }
else if (child instanceof InlineList inlineList) { else if (child instanceof InlineList inlineList) {
constantList.add(inlineList.getConstantValue()); constantList.add(inlineList.getConstantValue());
}
} }
this.constant = new TypedValue(Collections.unmodifiableList(constantList));
} }
return new TypedValue(Collections.unmodifiableList(constantList));
} }
@Override @Override

75
spring-expression/src/main/java/org/springframework/expression/spel/ast/InlineMap.java

@ -1,5 +1,5 @@
/* /*
* Copyright 2002-2021 the original author or authors. * Copyright 2002-2023 the original author or authors.
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@ -32,18 +32,19 @@ import org.springframework.util.Assert;
* *
* @author Andy Clement * @author Andy Clement
* @author Sam Brannen * @author Sam Brannen
* @author Harry Yang
* @since 4.1 * @since 4.1
*/ */
public class InlineMap extends SpelNodeImpl { public class InlineMap extends SpelNodeImpl {
// If the map is purely literals, it is a constant value and can be computed and cached // If the map is purely literals, it is a constant value and can be computed and cached
@Nullable @Nullable
private TypedValue constant; private final TypedValue constant;
public InlineMap(int startPos, int endPos, SpelNodeImpl... args) { public InlineMap(int startPos, int endPos, SpelNodeImpl... args) {
super(startPos, endPos, args); super(startPos, endPos, args);
checkIfConstant(); this.constant = computeConstantValue();
} }
@ -52,59 +53,55 @@ public class InlineMap extends SpelNodeImpl {
* contain constants, then a constant list can be built to represent this node. * contain constants, then a constant list can be built to represent this node.
* This will speed up later getValue calls and reduce the amount of garbage created. * This will speed up later getValue calls and reduce the amount of garbage created.
*/ */
private void checkIfConstant() { @Nullable
boolean isConstant = true; private TypedValue computeConstantValue() {
for (int c = 0, max = getChildCount(); c < max; c++) { for (int c = 0, max = getChildCount(); c < max; c++) {
SpelNode child = getChild(c); SpelNode child = getChild(c);
if (!(child instanceof Literal)) { if (!(child instanceof Literal)) {
if (child instanceof InlineList inlineList) { if (child instanceof InlineList inlineList) {
if (!inlineList.isConstant()) { if (!inlineList.isConstant()) {
isConstant = false; return null;
break;
} }
} }
else if (child instanceof InlineMap inlineMap) { else if (child instanceof InlineMap inlineMap) {
if (!inlineMap.isConstant()) { if (!inlineMap.isConstant()) {
isConstant = false; return null;
break;
} }
} }
else if (!(c % 2 == 0 && child instanceof PropertyOrFieldReference)) { else if (!(c % 2 == 0 && child instanceof PropertyOrFieldReference)) {
isConstant = false; return null;
break;
} }
} }
} }
if (isConstant) {
Map<Object, Object> constantMap = new LinkedHashMap<>(); Map<Object, Object> constantMap = new LinkedHashMap<>();
int childCount = getChildCount(); int childCount = getChildCount();
for (int c = 0; c < childCount; c++) { for (int c = 0; c < childCount; c++) {
SpelNode keyChild = getChild(c++); SpelNode keyChild = getChild(c++);
SpelNode valueChild = getChild(c); SpelNode valueChild = getChild(c);
Object key = null; Object key;
Object value = null; Object value = null;
if (keyChild instanceof Literal literal) { if (keyChild instanceof Literal literal) {
key = literal.getLiteralValue().getValue(); key = literal.getLiteralValue().getValue();
} }
else if (keyChild instanceof PropertyOrFieldReference propertyOrFieldReference) { else if (keyChild instanceof PropertyOrFieldReference propertyOrFieldReference) {
key = propertyOrFieldReference.getName(); key = propertyOrFieldReference.getName();
} }
else { else {
return; return null;
} }
if (valueChild instanceof Literal literal) { if (valueChild instanceof Literal literal) {
value = literal.getLiteralValue().getValue(); value = literal.getLiteralValue().getValue();
} }
else if (valueChild instanceof InlineList inlineList) { else if (valueChild instanceof InlineList inlineList) {
value = inlineList.getConstantValue(); value = inlineList.getConstantValue();
} }
else if (valueChild instanceof InlineMap inlineMap) { else if (valueChild instanceof InlineMap inlineMap) {
value = inlineMap.getConstantValue(); value = inlineMap.getConstantValue();
}
constantMap.put(key, value);
} }
this.constant = new TypedValue(Collections.unmodifiableMap(constantMap)); constantMap.put(key, value);
} }
return new TypedValue(Collections.unmodifiableMap(constantMap));
} }
@Override @Override

3
spring-expression/src/test/java/org/springframework/expression/spel/MapTests.java

@ -117,6 +117,9 @@ public class MapTests extends AbstractExpressionTests {
checkConstantMap("{#root.name:true}",false); checkConstantMap("{#root.name:true}",false);
checkConstantMap("{a:1,b:2,c:{d:true,e:false}}", true); checkConstantMap("{a:1,b:2,c:{d:true,e:false}}", true);
checkConstantMap("{a:1,b:2,c:{d:{1,2,3},e:{4,5,6},f:{'a','b','c'}}}", true); checkConstantMap("{a:1,b:2,c:{d:{1,2,3},e:{4,5,6},f:{'a','b','c'}}}", true);
// for nested InlineMap
checkConstantMap("{a:{k:#d}}", false);
checkConstantMap("{@bean:@bean}", false);
} }
private void checkConstantMap(String expressionText, boolean expectedToBeConstant) { private void checkConstantMap(String expressionText, boolean expectedToBeConstant) {

Loading…
Cancel
Save