Optimize InlineMap and InlineList in SpEL
- Make InlineList#constant and InlineMap#constant final. - Add more assertions for InlineMap tests. Closes gh-30251
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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) {
|
|
||||||
key = propertyOrFieldReference.getName();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (valueChild instanceof Literal literal) {
|
|
||||||
value = literal.getLiteralValue().getValue();
|
|
||||||
}
|
|
||||||
else if (valueChild instanceof InlineList inlineList) {
|
|
||||||
value = inlineList.getConstantValue();
|
|
||||||
}
|
|
||||||
else if (valueChild instanceof InlineMap inlineMap) {
|
|
||||||
value = inlineMap.getConstantValue();
|
|
||||||
}
|
|
||||||
constantMap.put(key, value);
|
|
||||||
}
|
}
|
||||||
this.constant = new TypedValue(Collections.unmodifiableMap(constantMap));
|
else if (keyChild instanceof PropertyOrFieldReference propertyOrFieldReference) {
|
||||||
|
key = propertyOrFieldReference.getName();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (valueChild instanceof Literal literal) {
|
||||||
|
value = literal.getLiteralValue().getValue();
|
||||||
|
}
|
||||||
|
else if (valueChild instanceof InlineList inlineList) {
|
||||||
|
value = inlineList.getConstantValue();
|
||||||
|
}
|
||||||
|
else if (valueChild instanceof InlineMap inlineMap) {
|
||||||
|
value = inlineMap.getConstantValue();
|
||||||
|
}
|
||||||
|
constantMap.put(key, value);
|
||||||
}
|
}
|
||||||
|
return new TypedValue(Collections.unmodifiableMap(constantMap));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user