如何在EL中引用常量?


106

如何在JSP页面上使用EL引用常量?

我有一个Addresses名为的常量接口URL。我知道我可以通过以下代码来引用它:<%=Addresses.URL%>但是如何使用EL做到这一点?

Answers:


156

EL 3.0或更高版本

如果您已经在使用Java EE 7 / EL 3.0,那么@page import还将在EL作用域中导入类常量。

<%@ page import="com.example.YourConstants" %>

这将在掩护下通过导入,ImportHandler#importClass()并以形式提供${YourConstants.FOO}

请注意,所有java.lang.*类均已隐式导入,并且可以像${Boolean.TRUE}and和那样使用${Integer.MAX_VALUE}。这仅需要更新的Java EE 7容器服务器,因为早期版本存在错误。例如,GlassFish 4.0和Tomcat 8.0.0-1x失败,但是GlassFish 4.1+和Tomcat 8.0.2x +可以工作。而且,您需要绝对确保声明您web.xml的声明符合服务器支持的最新servlet版本。因此web.xml,对于声明为符合Servlet 2.5或更早版本的,Servlet 3.0+的任何功能都将无法使用。

另请注意,此功能仅在JSP中可用,而在Facelets中不可用。对于JSF + Facelets,最好的选择是使用OmniFaces<o:importConstants>,如下所示:

<o:importConstants type="com.example.YourConstants" />

或添加ImportHandler#importClass()如下调用的EL上下文侦听器:

@ManagedBean(eager=true)
@ApplicationScoped
public class Config {

    @PostConstruct
    public void init() {
        FacesContext.getCurrentInstance().getApplication().addELContextListener(new ELContextListener() {
            @Override
            public void contextCreated(ELContextEvent event) {
                event.getELContext().getImportHandler().importClass("com.example.YourConstants");
            }
        });
    }

}

EL 2.2以上

这是不是可以在EL 2.2及以上。有几种选择:

  1. 将它们Map<String, Object>放在应用程序范围内。在EL中,可以通过${map.key}或JavaBean的常规方式访问映射值${map['key.with.dots']}

  2. 使用<un:useConstants>的的非标标签库(maven2的回购这里):

    <%@ taglib uri="http://jakarta.apache.org/taglibs/unstandard-1.0" prefix="un" %>
    <un:useConstants className="com.example.YourConstants" var="constants" />
    

    这样,就可以通过普通的Javabean方法访问它们${constants.FOO}

  3. 使用Javaranch的CCC <ccc:constantsMap>,如本文底部所述

    <%@ taglib uri="http://bibeault.org/tld/ccc" prefix="ccc" %>
    <ccc:constantsMap className="com.example.YourConstants" var="constants" />
    

    这样,它们也可以通过通常的Javabean方法进行访问${constants.FOO}

  4. 如果您正在使用JSF2,那么你可以使用<o:importConstants>OmniFaces

    <html ... xmlns:o="http://omnifaces.org/ui">
    <o:importConstants type="com.example.YourConstants" />
    

    这样,它们也可以通过通常的Javabean方法进行访问#{YourConstants.FOO}

  5. 创建一个包装器类,该包装器类通过Javabean风格的getter方法返回它们。

  6. 创建一个自定义EL解析器,该解析器首先扫描一个常量的存在,如果不存在,则委派给默认的解析器,否则返回常量值。


4
我发现此问题是因为尝试将静态List字段与form:options标记一起使用时遇到相同的问题。我可以通过添加返回静态列表的非静态getter使其工作。有点麻烦,但是,嘿,这是针对JSP的开发!
spaaarky21

1
如果bean是由spring管理的,那么您是否有示例如何为JSF配置它?提前谢谢。
Lodger

2
@Lodger:我不做Spring。
BalusC

2
雅加达unstandard-taglib项目还活着吗?还有其他选择吗?
davioooh 2014年

1
有没有办法将这种技术用于枚举?
尼古拉斯·彼得

11

以下内容通常不适用于EL,而是仅适用于SpEL(Spring EL)(已在Tomcat 7上用3.2.2.RELEASE测试)。我想在这里值得一提,以防有人搜索JSP和EL(但在Spring中使用JSP)。

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<spring:eval var="constant" expression="T(com.example.Constants).CONSTANT"/>

9

通常,您将这些类型的常量放置Configuration在Servlet上下文中的对象(具有getter和setter)中,并使用${applicationScope.config.url}


在谈到jsp时,这里有点新手-您能更全面地解释一下吗?
tau-neutrino

1
@ tau-neutrino:实际上很简单。创建一个具有urlString属性的类,将其命名为Configuration,将其实例化,然后将设置为所需的值url。之后,在中设置该Configuration对象ServletContext。做类似的事情servletContext.setAttribute("config", config)。然后你去。
阿迪尔·安萨里

您提出的解决方案与简单地将常数添加为ServletContext?的属性有什么区别?仅仅是可以更整齐地对常量进行分类吗?例如:applicationScope.config.urlvs applicationScope.url
他们



5

我实现像:

public interface Constants{
    Integer PAGE_SIZE = 20;
}

--

public class JspConstants extends HashMap<String, String> {

        public JspConstants() {
            Class c = Constants.class;
            Field[] fields = c.getDeclaredFields();
            for(Field field : fields) {
                int modifier = field.getModifiers();
                if(Modifier.isPublic(modifier) && Modifier.isStatic(modifier) && Modifier.isFinal(modifier)) {
                    try {
                        Object o = field.get(null);
                        put(field.getName(), o != null ? o.toString() : null);
                    } catch(IllegalAccessException ignored) {
                    }
                }
            }
        }

        @Override
        public String get(Object key) {
            String result = super.get(key);
            if(StringUtils.isEmpty(result)) {
                throw new IllegalArgumentException("Check key! The key is wrong, no such constant!");
            }
            return result;
        }
    }

下一步将此类的实例放入servlerContext

public class ApplicationInitializer implements ServletContextListener {


    @Override
    public void contextInitialized(ServletContextEvent sce) {
        sce.getServletContext().setAttribute("Constants", new JspConstants());
    }

    @Override
    public void contextDestroyed(ServletContextEvent sce) {
    }
}

将监听器添加到web.xml

<listener>
    <listener-class>com.example.ApplicationInitializer</listener-class>
</listener>

在jsp中访问

${Constants.PAGE_SIZE}

4

我一开始就在我的jsp中定义了一个常量:

<%final String URI = "http://www.example.com/";%>

我在我的JSP中包含了核心taglib:

<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

然后,通过以下语句使常量可用于EL:

<c:set var="URI" value="<%=URI%>"></c:set>

现在,我可以稍后使用。在下面的示例中,出于调试目的,该值只是作为HTML注释编写的:

<!-- ${URI} -->

使用常量类,您只需导入您的类并将常量分配给局部变量。我知道我的答案是一种快速技巧,但是当一个人想直接在JSP中定义常量时,这个问题也会浮出水面。


1
大声笑,如果您是这样初始化EL变量的,为什么不直接使用Scriptlets?
纳文

顶部的三行混乱不堪,然后在整个JSP ^^中清理EL。
koppor

1
@koppoor我猜是这样。我将使用<%=URI%>:P
Navin 2015年

1
我有一个地方,直接<%=URI%>行不通,但是这项技术行得通。
englebart

3

是的你可以。您需要一个自定义标签(如果在其他地方找不到它)。我已经做到了:

package something;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.Map;
import java.util.TreeMap;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

import org.apache.taglibs.standard.tag.el.core.ExpressionUtil;

/**
 * Get all class constants (statics) and place into Map so they can be accessed
 * from EL.
 * @author Tim.sabin
 */
public class ConstMapTag extends TagSupport {
    public static final long serialVersionUID = 0x2ed23c0f306L;

    private String path = "";
    private String var = "";

    public void setPath (String path) throws JspException {
        this.path = (String)ExpressionUtil.evalNotNull ("constMap", "path",
          path, String.class, this, pageContext);
    }

    public void setVar (String var) throws JspException {
        this.var = (String)ExpressionUtil.evalNotNull ("constMap", "var",
          var, String.class, this, pageContext);
    }

    public int doStartTag () throws JspException {
        // Use Reflection to look up the desired field.
        try {
            Class<?> clazz = null;
            try {
                clazz = Class.forName (path);
            } catch (ClassNotFoundException ex) {
                throw new JspException ("Class " + path + " not found.");
            }
            Field [] flds = clazz.getDeclaredFields ();
            // Go through all the fields, and put static ones in a Map.
            Map<String, Object> constMap = new TreeMap<String, Object> ();
            for (int i = 0; i < flds.length; i++) {
                // Check to see if this is public static final. If not, it's not a constant.
                int mods = flds [i].getModifiers ();
                if (!Modifier.isFinal (mods) || !Modifier.isStatic (mods) ||
                  !Modifier.isPublic (mods)) {
                    continue;
                }
                Object val = null;
                try {
                    val = flds [i].get (null);    // null for static fields.
                } catch (Exception ex) {
                    System.out.println ("Problem getting value of " + flds [i].getName ());
                    continue;
                }
                // flds [i].get () automatically wraps primitives.
                // Place the constant into the Map.
                constMap.put (flds [i].getName (), val);
            }
            // Export the Map as a Page variable.
            pageContext.setAttribute (var, constMap);
        } catch (Exception ex) {
            if (!(ex instanceof JspException)) {
                throw new JspException ("Could not process constants from class " + path);
            } else {
                throw (JspException)ex;
            }
        }
        return SKIP_BODY;
    }
}

标签称为:

<yourLib:constMap path="path.to.your.constantClass" var="consts" />

所有公共静态最终变量都将放入以Java名称索引的Map中,因此如果

public static final int MY_FIFTEEN = 15;

然后标签会将其包装在Integer中,您可以在JSP中引用它:

<c:if test="${consts['MY_FIFTEEN'] eq 15}">

而且您不必编写吸气剂!


3

您可以。按照尝试方式

 #{T(com.example.Addresses).URL}

在TomCat 7和Java6上测试


3
看起来像SpEL,而不是EL。我错了吗 另外,在较旧的Tomcat5.5中可以使用吗?
Pytry 2014年

2

甚至知道它有点晚,甚至知道这是一点小技巧-我使用以下解决方案来达到期望的结果。如果您是Java-Naming-Conventions的爱好者,我的建议是停止在这里阅读...

有一个这样的类,定义常量,按空类分组以创建某种层次结构:

public class PERMISSION{
    public static class PAGE{
       public static final Long SEE = 1L; 
       public static final Long EDIT = 2L; 
       public static final Long DELETE = 4L; 
       ...
    }
}

可以在java中用作PERMISSION.PAGE.SEE检索值1L

为了在EL-Expressions中实现类似的访问可能性,我做到了:(如果有编码神,他希望可以原谅我:D)

@Named(value="PERMISSION")
public class PERMISSION{
    public static class PAGE{
       public static final Long SEE = 1L; 
       public static final Long EDIT = 2L; 
       public static final Long DELETE = 4L; 
       ...

       //EL Wrapper
       public Long getSEE(){
           return PAGE.SEE;
       }

       public Long getEDIT(){
           return PAGE.EDIT;
       }

       public Long getDELETE(){
           return PAGE.DELETE;
       }
    }

    //EL-Wrapper
    public PAGE getPAGE() {
        return new PAGE();
    }
}

最终,访问相同的EL-Expression Long成为:#{PERMISSION.PAGE.SEE}-Java和EL-Access相等。我知道这是不合常规的,但是效果很好。


2

@Bozho已经提供了一个很好的答案

通常,您将这些类型的常量放置在Servlet上下文中的Configuration对象(具有getter和setter)中,然后使用$ {applicationScope.config.url}访问它们

但是,我认为需要一个示例,这样可以使您更加清楚并节省时间

@Component
public Configuration implements ServletContextAware {
    private String addressURL = Addresses.URL;

    // Declare other properties if you need as also add corresponding
    // getters and setters

    public String getAddressURL() {
        return addressURL;
    }

    public void setServletContext(ServletContext servletContext) {
        servletContext.setAttribute("config", this);
    }
}

0

有一种解决方法不完全是您想要的,但可以让您以极少的方式触摸脚本来激活几乎相同的方法。您可以使用scriptlet将值放入JSTL变量中,并在页面稍后使用干净的JSTL代码。

<%@ taglib prefix="c"       uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page import="com.whichever.namespace.Addresses" %>
<c:set var="ourUrl" value="<%=Addresses.URL%>"/>
<c:if test='${"http://www.google.com" eq ourUrl}'>
   Google is our URL!
</c:if>

1
我不明白为什么这被否决了。这与选项3中的模式相同:stackoverflow.com/a/16692821/274677
Marcus Junius Brutus
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.