我有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%>
。这些常量的值未显示。为什么这样做以及我该怎么做才能使它发挥作用?
答案 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的优雅方法,因此非常期待这类问题。