Как ссылаться на константы в EL?

106

Как ссылаться на константы с помощью EL на странице JSP?

У меня есть интерфейс Addressesс константой с именем URL. Я знаю, что могу сослаться на это с помощью скрипта, перейдя:, <%=Addresses.URL%>но как мне это сделать с помощью EL?

тау-нейтрино
источник

Ответы:

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}и ${Integer.MAX_VALUE}. Для этого требуется только более свежий контейнерный сервер Java EE 7, поскольку в ранних версиях были ошибки. Например, GlassFish 4.0 и Tomcat 8.0.0-1x не работают, но GlassFish 4.1+ и Tomcat 8.0.2x + работают. И вам нужно быть абсолютно уверенным в том, что ваш web.xmlобъявленный соответствует последней версии сервлета, поддерживаемой сервером. Таким образом, если web.xmlзаявлено, что сервер соответствует сервлету 2.5 или более ранней версии, ни одна из функций Servlet 3.0+ не будет работать.

Также обратите внимание, что эта возможность доступна только в JSP, но не в Facelets. В случае JSF + Facelets лучше всего использовать OmniFaces,<o:importConstants> как показано ниже:

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

Или добавьте слушателя контекста EL, который вызывает, ImportHandler#importClass()как показано ниже:

@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 значения карты доступны обычным способом Javabean с помощью ${map.key}или ${map['key.with.dots']}.

  2. Использование <un:useConstants>в нестандартном TagLib (Maven2 репо здесь ):

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

    Таким образом, они доступны обычным для Джавабея способом ${constants.FOO}.

  3. Используйте CCC <ccc:constantsMap> Javaranch, как описано где-то в конце этой статьи .

    <%@ 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.

  6. Создайте настраиваемый преобразователь EL, который сначала сканирует наличие константы и, если она отсутствует, затем делегирует преобразователю по умолчанию, в противном случае вместо этого возвращает постоянное значение.

BalusC
источник
4
Я нашел этот вопрос, потому что у меня была такая же проблема при попытке использовать статическое поле списка с тегом form: options. Мне удалось заставить его работать, добавив нестатический метод получения, который возвращает статический список. Это немного неуклюже, но, эй, это разработка JSP для тебя!
spaaarky21
1
У вас есть пример того, как настроить это для JSF, если beans управляется Spring? Спасибо заранее.
Lodger
2
@Lodger: Я не занимаюсь Spring.
BalusC
2
unstandard-taglibПроект в Джакарте еще жив? есть ли альтернатива?
davioooh
1
Есть ли способ использовать эти методы для перечислений?
Никлас Питер
11

Следующее не относится к EL в целом, а только к SpEL (Spring EL) (проверено с помощью 3.2.2.RELEASE на Tomcat 7). Я думаю, что об этом стоит упомянуть здесь, если кто-то ищет JSP и EL (но использует JSP с Spring).

<%@ taglib prefix="spring" uri="http://www.springframework.org/tags"%>
<spring:eval var="constant" expression="T(com.example.Constants).CONSTANT"/>
анре
источник
9

Обычно вы помещаете такие константы в Configurationобъект (который имеет геттеры и сеттеры) в контексте сервлета и получаете к ним доступ с помощью${applicationScope.config.url}

Божо
источник
Когда дело доходит до jsp, здесь немного новичка - не могли бы вы объяснить это более подробно?
тау-нейтрино
1
@ тау-нейтрино: На самом деле все просто. Создайте класс со urlсвойством String, назовите его Configuration, создайте его экземпляр и установите urlвсе, что вам нравится. После этого установите этот Configurationобъект ServletContext. Есть ли что - то подобное, servletContext.setAttribute("config", config). Вот и все.
Адил Ансари,
В чем разница между предложенным вами решением и простым добавлением константы в качестве атрибута ServletContext? Просто вы можете более аккуратно классифицировать константы? например: applicationScope.config.urlпротив applicationScope.url.
theyuv 08
7

Вы не можете. Он следует соглашению Java Bean. Так что у вас должен быть получатель для этого.

Адил Ансари
источник
5

Статические свойства недоступны в EL. Обходной путь, который я использую, - создать нестатическую переменную, которая присваивает себе статическое значение.

public final static String MANAGER_ROLE = 'manager';
public String manager_role = MANAGER_ROLE;

Я использую lombok для генерации геттера и сеттера, так что это очень хорошо. Ваш EL выглядит так:

${bean.manager_role}

Полный код на http://www.ninthavenue.com.au/java-static-constants-in-jsp-and-jsf-el

Роджер Кейс
источник
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 prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>

Затем я делаю константу доступной для EL с помощью следующего оператора:

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

Теперь я могу использовать его позже. Вот пример, где значение просто записано как HTML-комментарий для целей отладки:

<!-- ${URI} -->

С вашим постоянным классом вы можете просто импортировать свой класс и назначить константы локальным переменным. Я знаю, что мой ответ - это своего рода быстрый взлом, но вопрос также возникает, когда кто-то хочет определить константы непосредственно в JSP.

Коппор
источник
1
lol, почему бы не использовать скриптлеты напрямую, если именно так вы инициализируете переменные EL?
Navin
Три строки вверху - это беспорядок, а затем убирают EL во всем остальном JSP ^^.
koppor
1
@koppoor Думаю, да. Я просто собираюсь использовать <%=URI%>: P
Navin
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, поэтому, если

public static final int MY_FIFTEEN = 15;

тогда тег обернет это целым числом, и вы сможете ссылаться на него в JSP:

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

и вам не нужно писать геттеры!

Тим Сабин
источник
3

Ты можешь. Попробуйте следующим образом

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

Проверено на TomCat 7 и java6

Дмитрий Бойченко
источник
3
Это похоже на SpEL, а не на EL. Я ошибаюсь? Кроме того, будет ли это работать в более старом Tomcat5.5?
Pytry
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 уже дал отличный ответ

Обычно вы помещаете такие константы в объект Configuration (который имеет геттеры и сеттеры) в контексте сервлета и получаете к ним доступ с помощью $ {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

Существует обходной путь, который не совсем то, что вам нужно, но позволяет вам активировать почти то же самое, касаясь скриптлетов довольно минимальным образом. Вы можете использовать скриптлет, чтобы поместить значение в переменную 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
Марк Юний Брут,