Переменные в API javax.el - PullRequest
       16

Переменные в API javax.el

2 голосов
/ 23 февраля 2012

Я не очень понимаю, как переменные работают в javax.el:

// Implemented by the EL implementation:
ExpressionFactory factory = ExpressionFactory.newInstance();

// Implemented by the user:
ELContext context = ...;

Object result = factory.createValueExpression(context1, "${foo.bar}", Object.class).getValue1(context);

Почему необходимо дважды передавать контекст.Можно ли передать два разных контекста?Что используется для каких целей?Каков ожидаемый результат:

ValueExpression expr = factory.createValueExpression(context1, "${foo.bar}", Object.class).getValue(context2);

Javadoc ExpressionFactory # createValueExpression / JSR-245 объясняет, что:

The FunctionMapper and VariableMapper stored in the ELContext are used to resolve
functions and variables found in the expression. They can be null, in which case
functions or variables are not supported for this expression. The object returned
must invoke the same functions and access the same variable mappings regardless
of whether the mappings in the provided FunctionMapper and VariableMapper
instances change between calling ExpressionFactory.createValueExpression()
and any method on ValueExpression.

Более того, объясняет «Переменные EL JSR-245 2.0.7»:

An EL variable does not directly refer to a model object that can then be resolved
by an ELResolver. Instead, it refers to an EL expression. The evaluation of that
EL expression gives the EL variable its value.

[...]

[...] in this [...] example:
<c:forEach var=“item” items=“#{model.list}”>
    <h:inputText value=“#{item.name}”/>
 </c:forEach>

При создании выражения «# {item.name}» переменная «item» сопоставляется (в VariableMapper) с некоторым экземпляром ValueExpression, и выражение привязывается к этому экземпляру ValueExpression.Как создается это ValueExpression и как оно связано с различными элементами «model.list»?Как это должно быть реализовано?Можно создать ValueExpression один раз и повторно использовать его для каждой итерации:

 <!-- Same as above but using immediate evaluation -->
 <c:forEach var=“item” items=“${model.list}”>
    <h:inputText value=“${item.name}”/>
 </c:forEach>

ValueExpression e1 = factory.createExpression(context,"#{model.list}");
variableMapper.setVariable("item", ??);
ValueExpression e2 = factory.createExpression(context,"#{item.name}");
for(Object item : (Collection<?>) e1.getValue(context)) {
   ??
}

Или необходимо создать новое ValueExpression для итерации:

ValueExpression e1 = factory.createExpression(context,"#{model.list}");        
for(Object item : (Collection<?>) e1.getValue(context)) {
   variableMapper.setVariable("item", factory.createValueExpression(item,Object.class));
   ValueExpression e2 = factory.createExpression(context,"#{item.name}");
   Object name = e2.getValue(context);
   ...
}

1 Ответ

2 голосов
/ 25 марта 2012

Зачем нужно дважды передавать контекст.Можно ли передать два разных контекста?Что используется для каких целей?

variableMapper и functionMapper используются только во время анализа (factory.createMethod (...)), в то время как elResolver используется во время оценки (expr.getValue (...)).Можно использовать разные контексты для анализа и оценки выражения.

При создании выражения «# {item.name}» переменная «item» сопоставляется (в VariableMapper) с некоторым экземпляром ValueExpression.и выражение связано с этим экземпляром ValueExpression.Как создается это ValueExpression и как оно связано с различными элементами «model.list»?Как это должно быть реализовано?

Прежде всего: забудьте о «переменных».Как объясняется в Javadoc, их значения являются выражениями, предоставляемыми во время разбора.Думайте о переменных EL как о константах или макросах.Вы не можете переопределить их, они «записаны» в выражение.

Вам нужен механизм EL для разрешения свойств .В следующем рабочем примере я использую реализацию ELContext из JUEL , просто чтобы упростить задачу и сосредоточиться на релевантном использовании EL:

import java.util.Arrays;
import java.util.List;

import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;

import de.odysseus.el.util.SimpleContext;

public class Sof9404739 {
  /**
   * Sample item
   */
  public static class MyItem {
    String name;
    public MyItem(String name) {
      this.name = name;
    }
    public String getName() {
      return name;
    }
    public void setName(String name) {
      this.name = name;
    }
  }

  /**
   * Sample model
   */
  public static class MyModel {
    List<MyModel> list;
    public List<?> getList() {
      return list;
    }
    public void setList(List<MyModel> list) {
      this.list = list;
    }
  }

  /**
   * EL expression factory
   */
  static ExpressionFactory factory = ExpressionFactory.newInstance();

  public static void main(String[] args) {
    /**
     * Simple EL context implementation from JUEL
     */
    ELContext context = new SimpleContext();

    /**
     * Define the two expressions
     */
    ValueExpression listExpr =
      factory.createValueExpression(context, "#{model.list}", List.class);
    ValueExpression nameExpr =
      factory.createValueExpression(context, "#{item.name}", String.class);

    /**
     * This looks like a variable, but it isn't - it's a "root property"
     */
    context.getELResolver().setValue(context, null, "model", new MyModel());

    /**
     * Just for fun, initialize model's list property via EL, too
     */
    context.getELResolver().setValue(context, "model", "list",
        Arrays.asList(new MyItem("alice"), new MyItem("bob")));

    /**
     * Evaluate expressions like c:forEach
     */
    for (Object item : (List<?>) listExpr.getValue(context)) {
      /**
       * For each item, define the "item" (root) property
       */
      context.getELResolver().setValue(context, null, "item", item);
      System.out.println(nameExpr.getValue(context)); // "alice", "bob"
    }
  }
}
...