-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
Compile time annotation expression language #8323
Comments
Thanks for the write-up, this is a good start. Some thoughts: The context should be specified on the annotation member, so you would have something like: public @interface Scheduled {
@EvaluatedExpressionContext(CustomContext.class)
String initialDelay() default "";
} It could be on the annotation or the member level. One that is critical vs SpEL is that we want expression errors to fail at compilation time with errors to javac. In addition we should be able to create a context stub from a method signature. So for example if you have: @Secured("#{prinicipal.name == 'Fred'}")
@Get
String hello(Principal principal) {
...
} A binding is constructed with the types and names from the method parameters. For bean references it would be good to lookup beans by name but that could be something like |
@graemerocher
then it seems like some sort of resolution hierarchy has to be introduced here for cases when there are clashing names in evaluation context (e.g. method parameter has the same name as some property in annotation-level So it looks like precedence should be the following: Probably some way of referencing higher level of hierarcy should be provided as well. For instance, Spring allows to refer Regarding constructs for getting beans from context ( |
I would say if there are any conflicts there should be a compilation error |
From feature request #8323 this PR implements the following set of features (some parts will remain unimplemented in the first version): 1. Declaring literals. - [x] strings (delimited by single quotes) - [x] numeric types (int, long, float, double, incl. scientific notation and hex) - [x] boolean - [x] `null` 2. Mathematical operators - [x] `-`, `/`, `*`, `%`,`^` on numeric types - [x] `+` on both numeric types and strings 3. Relational operators - [x] Standart relational operators (`>`, `<`, `>=`, `<=`, `==`, `!=`) - [x] `instanceof` operator (e.g. `'abc' instanceof T(java.langString)`) - [x] `matches` for regex mathing (e.g. `'abc' matches '^[a-z]*'`) (including `/.+/` syntax for regex) - [x] `empty` for checking of an object is null or empty - [ ] Relational operators need to support comparables as well 4. Logical operators - [x] `&&`, `||`, `!`. We should probably also allow using aliases here (`and`, `or`, `not`) 5. Working with type references - [x] as part of `instanceof` operation - [x] as an argument for method invocation which is treated as io.micronaut.CutsomType.class (e.g. `getBean(T(io.micronaut.CustomType))` - [x] as a type reference followed by static method invocation (e.g. `T(java.lang.Math).random()`) 6. Working with collections - [x] Accessing by index for lists (e.g. `list[1]`) - [x] Accessing by key for maps (e.g. `map['key']`) - [ ] Collection filtering (e.g. `ages.filter(age -> age > 18)` ) - [ ] Collection mapping (e.g. `persons.map(p -> p.age)` ) For filtering/mapping probably makes sense to support lambda syntax. 7. Accessing object properties - [x] This should allow chaining property access with `.` (e.g. `object.property.nestedProperty`) - [x] Safe property access allowing to avoid NPE (`object?.property.?.nestedProperty`) 8. Methods invocation - [x] Object method invocation (e.g. `object.name().lenght()`) - [x] Static method invocation (`T(java.lang.Math).random()`) 9. Ternary operator - [x] Standard `expr ? 'trueResult' : 'falseResult'` - [x] Elvis operator (e.g. `age?:18`, note only makes sense if we implement coercion to boolean for numbers, lists, strings etc.) 10. Object construction - [ ] construction with `new` keyword (this might be useful in `@Value` annotation, e.g. `@Value(#{ new io.micronaut.CustomType() })` - [ ] array construcation (`new int[] {1, 2, 3}`) - not sure whether this is a useful one, since inlining list gives similar behavior - [ ] lists with `{1, 2, 3}` syntax - [ ] maps with `{ 'first': 1, 'second': 2 }` 11. Predefined syntax constructs, e.g. - [ ] Getting beans from context (e.g. `ctx[io.micronaut.CustomBean]`) - [ ] Accessing properties (e.g. `env['custom.property']` ) See #8323 --------- Co-authored-by: Sergey Gavrilov <[email protected]> Co-authored-by: Sergio del Amo <[email protected]>
Now when the basic implementation is merged in #8954, I've turned the original list of features into checklist. I'm planning to provide implementation for the following features:
Then I can also work on collections filtering and mapping, inlining lists and maps Regarding object construction with the What I also think is needed is support for expressions in |
I agree we can skip the Adding |
@GavrilovSV Going to close this, please create individual PRs for each enhancement. Thanks. |
By inlining lists and maps I mean the ability to declare collections in-place with syntax like |
Feature description
@graemerocher
Following the discussion started in #7897, this issue is aimed at working out a vision of annotation expression language similar to SpEL, but with most evaluation stages executed at compilation time.
By default an expression is annotation member value surrounded with
#{ }
. It would also be useful to introduce special annotation members which values will be treated as expressions without#{ }
. For example,@Requires(expr = "10 > 9")
The following is a set of features it is expected to include, mostly inspired by SpEL with certain adjustments
null
-
,/
,*
,%
,^
on numeric types+
on both numeric types and strings>
,<
,>=
,<=
,==
,!=
)instanceof
operator (e.g.'abc' instanceof T(java.langString)
)matches
for regex mathing (e.g.'abc' matches '^[a-z]*'
) (including/.+/
syntax for regex)empty
for checking of an object is null or empty&&
,||
,!
. We should probably also allow using aliases here (and
,or
,not
)Type reference should be provided in format of
T(io.micronaut.CustomType)
(allowing to omit full package forjava.lang
classes) and should be treated in different way depending on contextinstanceof
operationgetBean(T(io.micronaut.CustomType))
T(java.lang.Math).random()
){1, 2, 3}
syntax{ 'first': 1, 'second': 2 }
Nested collections should also be supported
list[1]
)map['key']
)ages[age > 18]
)persons[person.age]
).
(e.g.object.property.nestedProperty
)object?.property.?.nestedProperty
)object.name().lenght()
)T(java.lang.Math).random()
)expr ? 'trueResult' : 'falseResult'
age?:18
)new
keyword (this might be useful in@Value
annotation, e.g.@Value(#{ new io.micronaut.CustomType() })
new int[] {1, 2, 3}
) - not sure whether this is a useful one, since inlining list gives similar behaviorctx[T(io.micronaut.CustomBean)]
)properties['custom.property' ]
)EvaluationContext
against which expressions are evaluated. Evaluating against context allows referencing functions and variables registered in evaluation context using the#
sigh followed by function invocation/variable name. New context members can be registered at runtime.In case of micronaut it has to be implemented in different way. One option would be to annotate a class with a special annotation e.g.
@EvaluatedExpressionContext
making the annotated class a bean itself. At compilation time we can find annotated classes and build a compile-time evaluation context allowing fields/methods/properties be referenced in expressions using#
symbol. Under the hood referencing context method/field/property can be compiled into obtaining bean from context and accessing references element.So in general that might look like this
which can be compiled into something like
To make context classes from other modules available, a service descriptor reference can be generated so other modules will be able to discover context classes at compile time. However, that will require to include these modules as
annotationProcessor
s, which can be a downside. Anyway, this is only a concept of how that might look like, other options can be discussed as well.Features of SpEL not mentined above
@
symbol followed by bean name. Since micronaut treat bean names as qualifiers I don't think that is required and can be implemented.EvaluationContext
. Don't think it is required as wellI'll link a PR to this issue where I partly implemented points 1, 2, 3, 4, 5, 8, 9, 13 as a proof of concept without diving too deep into details. That PR can be a starting point
The text was updated successfully, but these errors were encountered: