4

我有我的Utility类的静态导入:

<%@ page import="static com.groupgti.webclient.util.MappingUtils.*" %>

我在这个类中有一些常量:

public static final String HTM = ".htm";

public static final String SECURE_REQUEST_MAPPING = "/secure/";

public static final String CANDIDATE_DETAILS_DATA_FORM_MAPPING = "assessments/candidate-details-data";

我有一个 Spring MVC 表单:

<form:form cssClass="springForm"
        action="${pageContext.servletContext.contextPath}<%=SECURE_REQUEST_MAPPING + CANDIDATE_DETAILS_DATA_FORM_MAPPING + HTM%>"
        commandName="assessments/candidate-details-request">
</form:form>

为什么当我这样使用时:

<a href="${pageContext.servletContext.contextPath}<%=SECURE_REQUEST_MAPPING + CANDIDATE_DETAILS_DATA_FORM_MAPPING + HTM%>">
   some text
</a>

属性值href生成正确,在 spring 表单action属性中 HTML 代码是这样的:/webclient<%=SECURE_REQUEST_MAPPING + CANDIDATE_DETAILS_DATA_FORM_MAPPING + HTM%>. 未显示这些常数的值。为什么会这样,我应该怎么做才能让它发挥作用?

4

2 回答 2

2

由于在工作中我们不允许在 JSP 中内联 Java,所以我使用自定义标签在 JSP 中“导入”静态最终类字段。UseConstantsTag它使用 Spring Framework 的 utils 和通用支持从废弃的 Jakarta 'unstandard' custom tag library API略微修改。(目前我什至找不到原始代码;这里是原始 API 文档。)

这个标签基本上暴露了所有静态最终字段(通过反射),Map它们可以很容易地在 JSP 中使用。看看这个 gist 中的完整代码,本质是:

/**
 * Tag that exposes all of the public constants in a class as a map stored in
 * a scoped attribute. The scope may be specified, but defaults to page scope.
 * <p/>
 * Based on abandoned project taglibs-unstandard, specifically
 * {@code org.apache.taglibs.unstandard.UseConstantsTag}. Uses Spring's TagUtils
 * and ClassUtils instead of utils bundled in taglibs-unstandard, plus it
 * supports generics.
 * 
 * @see http://jakarta.apache.org/taglibs/unstandard
 */
public class UseConstantsTag extends TagSupport {
  private static final long serialVersionUID = 1L;

  /**
   * The fully qualified name of the Java class for which constants are to be
   * exposed.
   */
  private String className;

  /**
   * The scope in which the exposed map will be stored.
   */
  private String scope = TagUtils.SCOPE_PAGE;

  /**
   * The name of the scoped attribute in which the constants will be stored.
   */
  private String var;

  /**
   * Construct an instance of this class.
   */
  public UseConstantsTag() {
  }

  /**
   * Retrieve the name of the class for which constants are to be exposed.
   * 
   * @return The fully qualified class name.
   */
  public String getClassName() {
    return className;
  }

  /**
   * Set the name of the class for which constants are to be exposed.
   * 
   * @param className The fully qualified class name.
   */
  public void setClassName(final String className) {
    this.className = className;
  }

  /**
   * Retrieve the scope in which the exposed map will be stored.
   * 
   * @return The name of the scope.
   */
  public String getScope() {
    return scope;
  }

  /**
   * Set the scope in which the exposed map will be stored.
   * 
   * @param scope The name of the scope.
   */
  public void setScope(final String scope) {
    Assert.notNull(scope, "Scope cannot be null");
    this.scope = scope;
  }

  /**
   * Retrieve the variable name in which the exposed map will be stored.
   * 
   * @return The name of the variable.
   */
  public String getVar() {
    return var;
  }

  /**
   * Set the variable name in which the exposed map will be stored.
   * 
   * @param var The name of the variable.
   */
  public void setVar(final String var) {
    this.var = var;
  }

  /**
   * Expose the constants for a class as a scoped attribute.
   * 
   * @return A constant that identifies what the container should do next.
   * 
   * @throws JspException if a fatal error occurs.
   */
  @Override
  public int doStartTag() throws JspException {
    if (className != null && var != null) {
      Map<String, Object> constants;
      try {
        constants = ClassReflectionUtils.getClassConstants(className);
      } catch (final ClassNotFoundException e) {
        throw new JspTagException("Class not found: " + className, e);
      } catch (final IllegalArgumentException e) {
        throw new JspTagException("Illegal argument: " + className, e);
      } catch (final IllegalAccessException e) {
        throw new JspTagException("Illegal access: " + className, e);
      }
      if (!constants.isEmpty()) {
        pageContext.setAttribute(var, constants, TagUtils.getScope(scope));
      }
    }

    return SKIP_BODY;
  }

  /**
   * Free up any resources being used by this tag handler.
   */
  @Override
  public void release() {
    super.release();
    className = null;
    scope = null;
    var = null;
  }

}

/**
 * Utility class for working with Class instances.
 */
final class ClassReflectionUtils {

  /**
   * Private constructor to prevent instantiation of this class.
   */
  private ClassReflectionUtils() {
  }

  /**
   * Creates and returns a map of the names of public static final constants to
   * their values, for the specified class.
   * 
   * @param className The fully qualified name of the class for which the
   *                  constants should be determined
   * 
   * @return {@code Map<String, Object>} from constant names to values
   * @throws ClassNotFoundException
   * @throws IllegalAccessException
   * @throws IllegalArgumentException
   */
  public static Map<String, Object> getClassConstants(final String className)
      throws ClassNotFoundException, IllegalArgumentException,
      IllegalAccessException {
    final Map<String, Object> constants = new HashMap<String, Object>();
    final Class<?> clazz = ClassUtils.forName(className,
        ClassUtils.getDefaultClassLoader());

    for (final Field field : clazz.getFields()) {
      final int modifiers = field.getModifiers();
      if (Modifier.isPublic(modifiers) && Modifier.isStatic(modifiers)
          && Modifier.isFinal(modifiers)) {
        // null as argument because it's ignored when field is static
        final Object value = field.get(null);
        if (value != null) {
          constants.put(field.getName(), value);
        }
      }
    }
    return constants;
  }

}

标签定义:

<tag>
    <name>useConstants</name>
    <tag-class>com.github.xaerxess.UseConstantsTag</tag-class>
    <body-content>empty</body-content>

    <display-name>useConstants</display-name>

    <description>
    Exposes all of the public constants in a class as a map stored in
    a scoped attribute. The scope may be specified, but defaults to page
    scope.
     </description>

    <variable>
        <name-from-attribute>var</name-from-attribute>
        <variable-class>java.lang.Object</variable-class>
        <declare>true</declare>
        <scope>AT_BEGIN</scope>
        <description>The name of the attribute into which the map will be stored.</description>
    </variable>

    <attribute>
        <name>var</name>
        <required>yes</required>
        <rtexprvalue>no</rtexprvalue>
        <description>Name of the scoped attribute into which the map will be stored.</description>
    </attribute>

    <attribute>
        <name>className</name>
        <required>yes</required>
        <rtexprvalue>no</rtexprvalue>
        <description>Fully qualified name of the class from which constants will be extracted.</description>
    </attribute>

    <attribute>
        <name>scope</name>
        <required>no</required>
        <rtexprvalue>no</rtexprvalue>
        <description>Scope into which to store the map. Default is page scope.</description>
    </attribute>

    <example>
To expose all of the constants in the Integer class:
<![CDATA[<un:useConstants var="const" className="java.lang.Integer" />]]> 
    </example>
</tag>

像这样使用它:

<custom:useConstants var="MappingUtils" 
    className="com.groupgti.webclient.util.MappingUtils" scope="application" />

接着:

<p>My const: ${MappingUtils.SECURE_REQUEST_MAPPING}</p>
于 2012-09-27T11:39:09.737 回答
1

JSP 允许您<%= ... %>在自定义标记(例如 )的属性中使用 scriptlet 表达式 ( ) <form:form>,但它必须是属性的唯一内容。因此,您不能<%= ... %>在自定义标签的属性中将表达式与 EL 表达式或纯文本混合。

但是,您可以在常规 HTML 标记的属性中使用任何内容,因为这些标记对于 JSP 没有特殊含义,这就是它与<a>.

一种可能的解决方案是将 scriptlet 表达式的结果放入请求属性中并在 EL 表达式中使用它:

<c:set var = "url" 
    value = "<%=SECURE_REQUEST_MAPPING + CANDIDATE_DETAILS_DATA_FORM_MAPPING + HTM%>" />
<form:form action="${pageContext.servletContext.contextPath}${url}" ...>
    ...
</form:form>

或者,您可以选择在没有 EL 的情况下使用 scriptlet,例如,通过定义一个在您的 : 中附加上下文路径的方法MappingUtils

... <%= url(...) %> ...

请注意,由于历史原因(EL 表达式被设计为替换 scriptlet)JSP 没有提供混合 EL 和 scriptlet 的优雅方法,因此这类问题几乎是意料之中的。

于 2012-09-27T11:35:39.997 回答