nested el variables for jsp taglibs

2019-09-01 16:24发布

问题:

I'm trying to create an el variable that is only available inside a tag so that it would be something like:

<mytaglib:mytag>
  Foo is ${foo}
</mytaglib:mytag>

I've followed the documentation I can find for creating a nested el variable, however, ${foo} is available after the end of mytag.

Shortened version of the .tld file is:

<?xml version="1.0" encoding="UTF-8" ?>

<taglib version="2.0" xmlns="http://java.sun.com/xml/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-jsptaglibrary_2_0.xsd">
  <description>My Tags</description>
  <tlib-version>1.0</tlib-version>
  <jsp-version>2.0</jsp-version>
  <short-name>mytaglib</short-name>
  <uri>http://www.example.com/taglibs/mytaglib</uri>

  <tag>
    <name>mytag</name>
    <tag-class>com.example.tags.MyTag</tag-class>
    <body-content>JSP</body-content>

    <variable>
      <name-given>used</name-given>
      <variable-class>java.lang.String</variable-class>
      <scope>NESTED</scope>
    </variable>
  </tag>
</taglib>

then MyTag does the following ( I've removed all code that shouldn't be related ):

public class MyTag extends BodyTagSupport
{
  // these are subtags
  private JspFragment below;
  private JspFragment at;

  @Override
  public int doStartTag() throws JspException
  {
    return EVAL_BODY_BUFFERED;
  }

  @Override
  public int doEndTag() throws JspException
  {
    try
    {
      if (aBoolean)
      {
          below.invoke(pageContext.getOut());
      }
      else
      {
          at.invoke(pageContext.getOut());
      }
    }
    catch (IOException e)
    {
      throw new JspException(e);
    }

    return EVAL_PAGE;
  }

  @Override
  public void doInitBody() throws JspException
  {
    pageContext.setAttribute("used", "valueOfUsed");
  }

  @Override
  public int doAfterBody() throws JspException
  {
    try
    {
      bodyContent.writeOut(bodyContent.getEnclosingWriter());
    }
    catch (IOException e)
    {
      throw new JspException(e);
    }

    return SKIP_BODY;
  }
}

My understanding is that pageContext.setAttribute() from within doInitBody should be in a nested scope and you evaluate the actual body in doAfterBody. That should seem to be the case though as 'used' is available outside the scope of the tag.

General background... tag would look like (simplified form)

<mytaglib:mytag>
  Used is ${used}
</mytaglib:mytag>

or

<mytaglib:mytag>
  <mytaglib:at>
    You are at your limit, you have used: ${used}
  </mytaglib:at>
  <mytaglib:below>
    You are below your limit, you have used: ${used}
  </mytaglib:below>
</mytaglib:mytag>

How can I get ${used} to be scoped to mytag?

回答1:

I always reference the JSTL as the model for any tags I write. So my approach would be to look at the source code of a tag that does something similar, and copy that. The <c:forEach> tag is a perfect example.

The LoopTagSupport class in JSTL implements the interface javax.servlet.jsp.tagext.TryCatchFinally, and in the doFinally method it calls pageContext.removeAttribute() manually.

Quoting the JSTL1.1 source distribution:

/**
 * Removes any attributes that this LoopTagSupport set.
 *
 * <p> These attributes are intended to support scripting variables with
 * NESTED scope, so we don't want to pollute attribute space by leaving
 * them lying around.
 */
public void doFinally() {
    /*
     * Make sure to un-expose variables, restoring them to their
     * prior values, if applicable.
     */
    unExposeVariables();
}

/**
 * Removes page attributes that we have exposed and, if applicable,
 * restores them to their prior values (and scopes).
 */
private void unExposeVariables() {
    // "nested" variables are now simply removed
if (itemId != null)
        pageContext.removeAttribute(itemId, PageContext.PAGE_SCOPE);
if (statusId != null)
    pageContext.removeAttribute(statusId, PageContext.PAGE_SCOPE);
}


标签: jsp el taglib