Spring MVC表单操作元素中不显示静态常量的值

时间:2012-09-27 11:02:46

标签: java spring jsp spring-mvc

我有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属性的值,并且在弹簧形式action属性中,HTML代码如下所示:/webclient<%=SECURE_REQUEST_MAPPING + CANDIDATE_DETAILS_DATA_FORM_MAPPING + HTM%>。这些常量的值未显示。为什么这样做以及我该怎么做才能使它发挥作用?

2 个答案:

答案 0 :(得分:2)

由于在工作中我们不允许在JSP中内联Java,因此我使用自定义标记在JSP中“导入”静态最终类字段。它使用Spring Framework的utils和泛型支持,从废弃的Jakarta'非标准'自定义标记库API略微修改UseConstantsTag。 (我现在甚至找不到原始代码;这里是original API documentation。)

此标记基本上将所有静态最终字段(通过反射)公开为Map,可以很容易地在JSP中使用。 Take a look at full code in this 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>

答案 1 :(得分: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的优雅方法,因此非常期待这类问题。