-
Notifications
You must be signed in to change notification settings - Fork 682
Add ValueExpression infrastructure #3036
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Value Expressions a composition of {spring-framework-docs}/core/expressions.html[Spring Expression Language (SpEL)] and {spring-framework-docs}/core/beans/environment.html#beans-placeholder-resolution-in-statements[Property Placeholder Resolution]. | ||
Value Expressions a combine powerful evaluation of programmatic expressions with the simplicity to resort to property-placeholder resolution to obtain values from the `Environment` such as configuration properties. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Those do not read well, starting sentences right after one another with same Value Expressions. 1st one also misses a verb and in the 2nd the a should be removed.
<1> Value Expression using a single SpEL Expression. | ||
<2> Value Expression using a static SpEL Expression evaluating to `2-hello-world`. | ||
<3> Value Expression using a single Property Placeholder. | ||
<4> Composite expression comprised from the literal `orders-` and the Property Placeholder `${tenant-config.suffix}`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should this be comprised of
?
==== | ||
|
||
Using value expressions introduces a lot of flexibility to your code. | ||
Keep in mind that using value expressions requires evaluation of the expression on each usage. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we stress this a bit more by moving it to a dedicated callout?
assertThat(eval("#{\"foo\"}-")).isEqualTo("foo-"); | ||
assertThat(eval("#{\"fo'o\"}-")).isEqualTo("fo'o-"); | ||
assertThat(eval("${key.one}-")).isEqualTo("value-"); | ||
assertThat(eval("#{#foo}")).isEqualTo("bar"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we should also add tests that reference properties that do not exist.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
value defaulting would also be good to check ${non-existing.key:defaultValue}
@BeforeEach | ||
void setUp() { | ||
|
||
MapPropertySource propertySource = new MapPropertySource("map", Map.of("key.one", "value")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MapPropertySource
may contain non String
values. What is the expected output when evaluating expressions like "${numeric.key}"
having "numeric.key", 1L
?
assertThat(eval("#{\"foo\"}-")).isEqualTo("foo-"); | ||
assertThat(eval("#{\"fo'o\"}-")).isEqualTo("fo'o-"); | ||
assertThat(eval("${key.one}-")).isEqualTo("value-"); | ||
assertThat(eval("${key.one}-and-some-#{'foo'}-#{#foo}")).isEqualTo("value-and-some-foo-bar"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it allowed/possible to reference properties from within an expression? like #{#foo - ${numeric.key}}
?
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
package org.springframework.data.spel; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shall we use expression
instead of spel
for the package name?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I was wondering, too. What about the spel
package then? I think we should migrate it into ….expression
as well in a compatible way.
} | ||
|
||
@Override | ||
public void setEnvironment(Environment environment) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
though it's an override, we could add some doc around it having a since
tag.
@@ -137,14 +141,20 @@ public void setApplicationContext(ApplicationContext applicationContext) throws | |||
if (applicationEventPublisher == null) { | |||
this.applicationEventPublisher = applicationContext; | |||
} | |||
|
|||
this.environment = applicationContext.getEnvironment(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
which one wins? this one or setEnvironment
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is an ambiguity overall. We could implement BeanFactoryAware
and set the environment from setApplicationContext
only if it wasn't already set.
while (startIndex != -1) { | ||
|
||
int endIndex = findPlaceholderEndIndex(expression, startIndex); | ||
if (endIndex != -1) { | ||
|
||
String part = expression.substring(startIndex, endIndex + 1); | ||
|
||
if (part.startsWith(PLACEHOLDER_PREFIX)) { | ||
expressions.add(createPlaceholder(part)); | ||
} else { | ||
expressions.add(createExpression(part)); | ||
} | ||
|
||
placerholderIndex = expression.indexOf(PLACEHOLDER_PREFIX, endIndex); | ||
expressionIndex = expression.indexOf(EXPRESSION_PREFIX, endIndex); | ||
|
||
startIndex = getStartIndex(placerholderIndex, expressionIndex); | ||
|
||
if (startIndex == -1) { | ||
expressions.add(new LiteralValueExpression(expression.substring(endIndex + 1))); | ||
} else { | ||
expressions.add(new LiteralValueExpression(expression.substring(endIndex + 1, startIndex))); | ||
} | ||
} else { | ||
expressions.add(new LiteralValueExpression(expression.substring(startIndex))); | ||
startIndex = -1; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this part is really hard to grasp - care to leave some comments in there please.
like why do we substring from end to start here expression.substring(endIndex + 1, startIndex))
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure. +1
is to skip the closing parenthesis and to not include it in the literal.
b4d4d4b
to
622087a
Compare
LGTM 👍 |
We now support parsing of value expressions in the context of persistent entities and persistent properties. Value expressions are literals, simple expressions or composite expressions that can refer to SpEL expressions or property placeholders resolved against the
Environment
:Closes #2369