Como referenciar constantes em EL?

106

Como você faz referência a constantes com EL em uma página JSP?

Eu tenho uma interface Addressescom uma constante chamada URL. Eu sei que posso referenciá-lo com um scriplet indo:, <%=Addresses.URL%>mas como faço isso usando EL?

tau-neutrino
fonte

Respostas:

156

EL 3.0 ou mais recente

Se você já estiver no Java EE 7 / EL 3.0, o @page importtambém importará constantes de classe no escopo EL.

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

Isso estará sob as capas importado por ImportHandler#importClass()e estará disponível como ${YourConstants.FOO}.

Observe que todas as java.lang.*classes já foram importadas implicitamente e estão disponíveis como ${Boolean.TRUE}e ${Integer.MAX_VALUE}. Isso requer apenas um servidor de contêiner Java EE 7 mais recente, pois as versões anteriores apresentavam bugs. Por exemplo, GlassFish 4.0 e Tomcat 8.0.0-1x falham, mas GlassFish 4.1+ e Tomcat 8.0.2x + funcionam. E você precisa ter certeza absoluta de que web.xmlestá em conformidade com a versão mais recente do servlet suportada pelo servidor. Assim, com um web.xmlServlet 2.5 ou anterior declarado conforme, nenhum dos recursos do Servlet 3.0+ funcionará.

Observe também que esse recurso está disponível apenas em JSP e não em Facelets. No caso de JSF + Facelets, sua melhor aposta é usar OmniFaces<o:importConstants> conforme abaixo:

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

Ou adicionando um ouvinte de contexto EL que chama ImportHandler#importClass()conforme abaixo:

@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 ou mais antigo

Isso não é possível no EL 2.2 e anteriores. Existem várias alternativas:

  1. Coloque-os em um Map<String, Object>que você colocou no escopo do aplicativo. Em EL, os valores do mapa são acessíveis da maneira usual de Javabean por ${map.key}ou ${map['key.with.dots']}.

  2. Uso <un:useConstants>do taglib Unstandard (maven2 repo aqui ):

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

    Desta forma, eles são acessíveis da maneira usual de Javabean ${constants.FOO}.

  3. Use o CCC de Javaranch <ccc:constantsMap>conforme descrito em algum lugar no final deste artigo .

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

    Desta forma, eles também são acessíveis da maneira usual de Javabean ${constants.FOO}.

  4. Se você estiver usando JSF2, então você poderia usar <o:importConstants>de OmniFaces .

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

    Desta forma, eles também são acessíveis da maneira usual de Javabean #{YourConstants.FOO}.

  5. Crie uma classe de wrapper que os retorne por meio de métodos getter no estilo javabeano.

  6. Crie um resolvedor EL personalizado que primeiro verifica a presença de uma constante e, se ausente, delega ao resolvedor padrão; caso contrário, retorna o valor da constante.

BalusC
fonte
4
Eu encontrei essa pergunta porque estava tendo o mesmo problema ao tentar usar um campo List estático com uma tag form: options. Consegui fazê-lo funcionar adicionando um getter não estático que retorna a lista estática. É um pouco confuso, mas olha, isso é desenvolvimento JSP para você!
spaaarky21
1
Você tem algum exemplo de como configurar isso para JSF se os beans forem gerenciados até a primavera? Thx antecipadamente.
Lodger de
2
@Lodger: Eu não faço Spring.
BalusC de
2
O unstandard-taglibprojeto de Jacarta ainda está vivo? existe alguma alternativa?
davioooh
1
Existe uma maneira de aproveitar essas técnicas para enums?
Niklas Peter
11

O seguinte não se aplica ao EL em geral, mas ao invés do SpEL (Spring EL) somente (testado com 3.2.2.RELEASE no Tomcat 7). Acho que vale a pena mencioná-lo aqui caso alguém procure por JSP e EL (mas usa JSP com Spring).

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

Você geralmente coloca esses tipos de constantes em um Configurationobjeto (que tem getters e setters) no contexto do servlet e os acessa com${applicationScope.config.url}

Bozho
fonte
Um pouco novato aqui quando se trata de jsp's - você poderia explicar isso de forma mais completa?
tau-neutrino de
1
@ tau-neutrino: É simples, na verdade. Crie uma classe com urluma propriedade String, nomeie-a Configuration, instancie-a e defina urlcomo quiser. Depois disso, coloque esse Configurationobjeto em ServletContext. Faça algo como servletContext.setAttribute("config", config),. E aí está.
Adeel Ansari
Qual é a diferença entre sua solução proposta e simplesmente adicionar a constante como um atributo do ServletContext? É só que você pode classificar as constantes de maneira mais precisa? por exemplo: applicationScope.config.urlvs applicationScope.url.
theyuv
7

Você não pode. Ele segue a convenção Java Bean. Portanto, você deve ter um getter para isso.

Adeel Ansari
fonte
5

As propriedades estáticas não são acessíveis em EL. A solução alternativa que uso é criar uma variável não estática que se atribui ao valor estático.

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

Eu uso o lombok para gerar o getter e o setter, então é isso. Seu EL é parecido com este:

${bean.manager_role}

Código completo em http://www.ninthavenue.com.au/java-static-constants-in-jsp-and-jsf-el

Roger Keays
fonte
5

Eu implementei como:

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;
        }
    }

Próxima etapa colocar a instância desta classe em servlerContext

public class ApplicationInitializer implements ServletContextListener {


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

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

adicionar ouvinte a web.xml

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

acesso em jsp

${Constants.PAGE_SIZE}
Serhii Bohutskyi
fonte
4

Estou definindo uma constante em meu jsp logo no início:

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

Incluo o taglib principal em meu JSP:

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

Em seguida, disponibilizo a constante para EL seguindo a seguinte declaração:

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

Agora, posso usá-lo mais tarde. Aqui está um exemplo, onde o valor é escrito apenas como comentário HTML para fins de depuração:

<!-- ${URI} -->

Com sua classe constante, você pode simplesmente importar sua classe e atribuir as constantes às variáveis ​​locais. Eu sei que minha resposta é uma espécie de hack rápido, mas a questão também surge quando se quer definir constantes diretamente no JSP.

koppor
fonte
1
lol por que não usar Scriptlets diretamente se é assim que você inicializa as variáveis ​​EL?
Navin
Três linhas no topo são uma bagunça e então limpe EL em todo o resto do JSP ^^.
koppor de
1
@koppoor Acho que sim. Vou usar apenas <%=URI%>: P
Navin
1
Eu tinha um lugar onde um direto <%=URI%>não funcionava, mas essa técnica funcionava.
Englebart
3

Sim você pode. Você precisa de uma tag personalizada (se não conseguir encontrá-la em outro lugar). Eu fiz isso:

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;
    }
}

e a tag é chamada:

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

Todas as variáveis ​​finais estáticas públicas serão colocadas em um mapa indexado por seu nome Java, portanto, se

public static final int MY_FIFTEEN = 15;

então a tag envolverá isso em um inteiro e você pode referenciá-lo em um JSP:

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

e você não precisa escrever getters!

Tim Sabin
fonte
3

Você pode. Tente seguir o caminho

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

Testado em TomCat 7 e java6

Dmytro Boichenko
fonte
3
Isso se parece com SpEL, e não EL. Eu estou enganado? Além disso, isso funcionaria em um Tomcat5.5 mais antigo?
Pytry
2

Mesmo sabendo que é um pouco tarde, e mesmo sabendo que isso é um pequeno hack - usei a seguinte solução para alcançar o resultado desejado. Se você é um amante de Java-Naming-Conventions, meu conselho é parar de ler aqui ...

Ter uma classe como essa, definindo constantes, agrupadas por classes vazias para criar uma espécie de hierarquia:

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; 
       ...
    }
}

pode ser usado de dentro de java PERMISSION.PAGE.SEEpara recuperar o valor1L

Para conseguir uma possibilidade de acesso semelhante de dentro de EL-Expressions, eu fiz o seguinte: (Se houver um deus da codificação - espero que ele me perdoe: 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();
    }
}

finalmente, a EL-Expression para acessar o mesmo Longtorna-se: #{PERMISSION.PAGE.SEE}- igualdade para Java e EL-Access. Eu sei que isso está fora de qualquer convenção, mas funciona perfeitamente bem.

dognose
fonte
2

@Bozho já deu uma ótima resposta

Você geralmente coloca esses tipos de constantes em um objeto de configuração (que tem getters e setters) no contexto do servlet e os acessa com $ {applicationScope.config.url}

No entanto, sinto que é necessário um exemplo para trazer um pouco mais de clareza e poupar o tempo de alguém

@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);
    }
}
lunohodov
fonte
0

Há uma solução alternativa que não é exatamente o que você deseja, mas permite que você ative quase da mesma forma, tocando scriptlets de uma maneira mínima. Você pode usar o scriptlet para colocar o valor em uma variável JSTL e usar o código JSTL limpo posteriormente na página.

<%@ 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>
Artem
fonte
1
Não vejo porque foi rejeitado. Este é o mesmo padrão da opção nº 3 em: stackoverflow.com/a/16692821/274677
Marcus Junius Brutus