Class ExecutableMethodELExecutor

java.lang.Object
jakarta.el.ELResolver
io.micronaut.el.resolver.ExecutableMethodELExecutor
All Implemented Interfaces:
io.micronaut.core.order.Ordered, ELMethodExecutor

public final class ExecutableMethodELExecutor extends jakarta.el.ELResolver implements ELMethodExecutor
The ELMethodExecutor invoking a method of a bean through the executable metadata its bean definition carries, the metadata Micronaut generates for every method annotated with io.micronaut.context.annotation.Executable, directly or through an annotation that is itself meta-annotated with it.

A bean introspection and an executable method are two independent descriptions of a type, and a great many beans carry the second without the first: anything AOP-advised, anything a framework marks executable for its own dispatch. This executor makes such a method reachable from an expression without the type also having to be annotated @Introspected.

The class is both an ELMethodExecutor and an ELResolver because the two execution paths of this project reach a method differently: an expression parsed at runtime resolves it through the ELMethodExecutor services, while an expression compiled at compilation time dispatches through the resolver chain of its context, ELResolution.invoke calling ELContext.getELResolver().invoke. One implementation answers both, so a method resolves the same way whichever path an expression took.

The executor is registered in META-INF/services/io.micronaut.el.ELMethodExecutor, so it is available to every expression parsed at runtime with no wiring at all. Registered that way it is constructed with the no-argument constructor and has no bean context of its own, so it reads one from the ELContext of each call, which an application registers once:

context.putContext(BeanDefinitionRegistry.class, beanContext);

BeanContext.class works as the key as well. A context that carries neither is not one this executor can read, so it declines and the rest of the chain resolves the method as it did before. Reading the registry from the context rather than from a static holder is what lets an application that runs more than one bean context resolve a method in the context the expression is evaluated for; the ExecutableMethodELExecutor(BeanDefinitionRegistry) constructor binds one directly, for the resolver chain and for an application registering the executor programmatically.

The executor only invokes: the properties stay with the rest of the chain. A base whose class is not a bean of the context has no definition, so the executor declines and the reflective one gets its chance. The runtime class of an intercepted instance is the generated proxy, whose definition is a ProxyBeanDefinition; the executor reads the executable methods of the target definition, so the method that is found is the one the author wrote.

Like IntrospectionELResolver, the executor reads compiled metadata rather than reflection, so it works under a GraalVM native image without any registration.

Since:
1.1
Author:
Denis Stepanov
  • Field Summary

    Fields inherited from interface io.micronaut.core.order.Ordered

    HIGHEST_PRECEDENCE, LOWEST_PRECEDENCE
  • Constructor Summary

    Constructors
    Constructor
    Description
    Creates the executor reading, on every call, the registry registered in the ELContext the expression is evaluated with, under the key BeanDefinitionRegistry.class or BeanContext.class.
    ExecutableMethodELExecutor(io.micronaut.context.BeanDefinitionRegistry registry)
    Creates the executor reading the definitions of a registry.
  • Method Summary

    Modifier and Type
    Method
    Description
    @Nullable Class<?>
    getCommonPropertyType(jakarta.el.ELContext context, @Nullable Object base)
     
    int
    Returns the executor order.
    @Nullable Class<?>
    getType(jakarta.el.ELContext context, @Nullable Object base, @Nullable Object property)
     
    @Nullable Object
    getValue(jakarta.el.ELContext context, @Nullable Object base, @Nullable Object property)
     
    @Nullable Object
    invoke(jakarta.el.ELContext context, @Nullable Object base, @Nullable Object method, Class<?> @Nullable [] paramTypes, Object @Nullable [] params)
     
    boolean
    isReadOnly(jakarta.el.ELContext context, @Nullable Object base, @Nullable Object property)
     
    @Nullable ELMethod
    resolve(jakarta.el.ELContext context, @Nullable Object base, @Nullable Object method, io.micronaut.core.type.Argument<?> @Nullable [] argumentTypes, Object @Nullable [] arguments)
    Resolves an instance or static method, or a constructor represented by <init>.
    void
    setValue(jakarta.el.ELContext context, @Nullable Object base, @Nullable Object property, @Nullable Object value)
     

    Methods inherited from class jakarta.el.ELResolver

    convertToType

    Methods inherited from class Object

    clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait

    Methods inherited from interface ELMethodExecutor

    isReflective, resolveFunction
  • Constructor Details

    • ExecutableMethodELExecutor

      public ExecutableMethodELExecutor()
      Creates the executor reading, on every call, the registry registered in the ELContext the expression is evaluated with, under the key BeanDefinitionRegistry.class or BeanContext.class.

      This is the constructor the service loader uses. An expression evaluated with a context that carries no registry resolves the way it did before this executor existed.

    • ExecutableMethodELExecutor

      public ExecutableMethodELExecutor(io.micronaut.context.BeanDefinitionRegistry registry)
      Creates the executor reading the definitions of a registry.

      A io.micronaut.context.BeanContext is one, and is what an application usually has. The narrower type is what this executor actually needs: it reads the executable metadata of a definition and invokes the method on the instance the expression produced, so it never looks a bean up, creates one, or touches the lifecycle of the context.

      Parameters:
      registry - The registry whose definitions carry the executable methods
  • Method Details

    • getOrder

      public int getOrder()
      Description copied from interface: ELMethodExecutor
      Returns the executor order. Lower values run first, following the Micronaut Ordered contract, so direct/generated implementations can take precedence over a general fallback such as reflection.
      Specified by:
      getOrder in interface ELMethodExecutor
      Specified by:
      getOrder in interface io.micronaut.core.order.Ordered
      Returns:
      The order, zero by default
    • invoke

      public @Nullable Object invoke(jakarta.el.ELContext context, @Nullable Object base, @Nullable Object method, Class<?> @Nullable [] paramTypes, Object @Nullable [] params)
      Overrides:
      invoke in class jakarta.el.ELResolver
    • resolve

      public @Nullable ELMethod resolve(jakarta.el.ELContext context, @Nullable Object base, @Nullable Object method, io.micronaut.core.type.Argument<?> @Nullable [] argumentTypes, Object @Nullable [] arguments)
      Description copied from interface: ELMethodExecutor
      Resolves an instance or static method, or a constructor represented by <init>.
      Specified by:
      resolve in interface ELMethodExecutor
      Parameters:
      context - The EL context
      base - The instance, or an ELClass for a static method or constructor
      method - The method name
      argumentTypes - The parameter types supplied when the method expression was created, or null
      arguments - The evaluated arguments, or null
      Returns:
      The resolved method, or null when this executor does not handle it
    • getValue

      public @Nullable Object getValue(jakarta.el.ELContext context, @Nullable Object base, @Nullable Object property)
      Specified by:
      getValue in class jakarta.el.ELResolver
    • getType

      public @Nullable Class<?> getType(jakarta.el.ELContext context, @Nullable Object base, @Nullable Object property)
      Specified by:
      getType in class jakarta.el.ELResolver
    • setValue

      public void setValue(jakarta.el.ELContext context, @Nullable Object base, @Nullable Object property, @Nullable Object value)
      Specified by:
      setValue in class jakarta.el.ELResolver
    • isReadOnly

      public boolean isReadOnly(jakarta.el.ELContext context, @Nullable Object base, @Nullable Object property)
      Specified by:
      isReadOnly in class jakarta.el.ELResolver
    • getCommonPropertyType

      public @Nullable Class<?> getCommonPropertyType(jakarta.el.ELContext context, @Nullable Object base)
      Specified by:
      getCommonPropertyType in class jakarta.el.ELResolver