¿Cómo hacer referencia a constantes en EL?

106

¿Cómo se hace referencia a una constante con EL en una página JSP?

Tengo una interfaz Addressescon una constante llamada URL. Sé que puedo hacer referencia a él con un scriplet yendo:, <%=Addresses.URL%>pero ¿cómo hago esto usando EL?

tau-neutrino
fuente

Respuestas:

156

EL 3.0 o más reciente

Si ya está en Java EE 7 / EL 3.0, entonces @page importtambién importará constantes de clase en el alcance EL.

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

Esto debajo de las cubiertas se importará a través de ImportHandler#importClass()y estará disponible como ${YourConstants.FOO}.

Tenga en cuenta que todas las java.lang.*clases ya están implícitamente importadas y disponibles como tal ${Boolean.TRUE}y ${Integer.MAX_VALUE}. Esto solo requiere un servidor de contenedor Java EE 7 más reciente, ya que las primeras versiones tenían errores en esto. Por ejemplo, GlassFish 4.0 y Tomcat 8.0.0-1x fallan, pero GlassFish 4.1+ y Tomcat 8.0.2x + funcionan. Y debe asegurarse absolutamente de que web.xmlse declara que cumple con la última versión de servlet admitida por el servidor. Por lo tanto, con un web.xmlServlet 2.5 declarado conforme o anterior, ninguna de las características de Servlet 3.0+ funcionará.

También tenga en cuenta que esta función solo está disponible en JSP y no en Facelets. En el caso de JSF + Facelets, su mejor opción<o:importConstants> es utilizar OmniFaces como se muestra a continuación:

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

O agregando un oyente de contexto EL que llama de la ImportHandler#importClass()siguiente manera:

@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 o mayor

Esto no es posible en EL 2.2 y versiones anteriores. Existen varias alternativas:

  1. Colóquelos en el Map<String, Object>que puso en el ámbito de la aplicación. En EL, los valores del mapa son accesibles de la forma habitual de Java por ${map.key}o ${map['key.with.dots']}.

  2. El uso <un:useConstants>de la unstandard taglib (maven2 Repo aquí ):

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

    De esta manera, son accesibles de la forma habitual en Java ${constants.FOO}.

  3. Utilice CCC de Javaranch <ccc:constantsMap>como se describe en alguna parte al final de este artículo .

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

    De esta forma, también son accesibles de la forma habitual en Java ${constants.FOO}.

  4. Si está utilizando JSF2, entonces se podría utilizar <o:importConstants>de OmniFaces .

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

    De esta forma, también son accesibles de la forma habitual en Java #{YourConstants.FOO}.

  5. Cree una clase contenedora que los devuelva a través de métodos getter de estilo Javabean.

  6. Cree un resolutor EL personalizado que primero escanee la presencia de una constante y, si está ausente, luego delegue al resolutor predeterminado; de lo contrario, devuelve el valor constante.

BalusC
fuente
4
Encontré esta pregunta porque estaba teniendo el mismo problema al intentar usar un campo List estático con una etiqueta form: options. Pude hacerlo funcionar agregando un captador no estático que devuelve la lista estática. Es un poco torpe, pero bueno, ¡eso es desarrollo JSP para ti!
spaaarky21
1
¿Tiene algún ejemplo de cómo configurar esto para JSF si los beans son administrados por Spring? Gracias de antemano.
Lodger
2
@Lodger: Yo no hago Spring.
BalusC
2
¿ unstandard-taglibSigue vivo el proyecto de Yakarta ? hay alguna alternativa?
davioooh
1
¿Hay alguna forma de aprovechar estas técnicas para enumeraciones?
Niklas Peter
11

Lo siguiente no se aplica a EL en general, sino a SpEL (Spring EL) solo (probado con 3.2.2.RELEASE en Tomcat 7). Creo que vale la pena mencionarlo aquí en caso de que alguien busque JSP y EL (pero usa JSP con Spring).

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

Por lo general, coloca este tipo de constantes en un Configurationobjeto (que tiene captadores y establecedores) en el contexto del servlet y accede a ellas con${applicationScope.config.url}

Bozho
fuente
Un poco novato aquí cuando se trata de jsp, ¿podría explicarlo con más detalle?
tau-neutrino
1
@ tau-neutrino: En realidad, es simple. Cree una clase con urluna propiedad String, asígnele un nombre Configuration, cree una instancia y establezca el valor urlque desee. Después de eso, coloque ese Configurationobjeto ServletContext. Hacer algo así, servletContext.setAttribute("config", config). Y ahí tienes.
Adeel Ansari
¿Cuál es la diferencia entre su solución propuesta y simplemente agregar la constante como un atributo de ServletContext? ¿Es solo que puede clasificar las constantes de manera más ordenada? por ejemplo: applicationScope.config.urlvs applicationScope.url.
theyuv
7

No puedes. Sigue la convención de Java Bean. Así que debes tener un captador para ello.

Adeel Ansari
fuente
5

Las propiedades estáticas no son accesibles en EL. La solución alternativa que utilizo es crear una variable no estática que se asigne al valor estático.

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

Yo uso lombok para generar el getter y setter, así que está bastante bien. Su EL se ve así:

${bean.manager_role}

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

Roger Keays
fuente
5

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

El siguiente paso coloca la instancia de esta clase en servlerContext

public class ApplicationInitializer implements ServletContextListener {


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

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

agregar oyente a web.xml

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

acceso en jsp

${Constants.PAGE_SIZE}
Serhii Bohutskyi
fuente
4

Estoy definiendo una constante en mi jsp desde el principio:

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

Incluyo el taglib principal en mi JSP:

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

Luego, pongo la constante a disposición de EL mediante la siguiente declaración:

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

Ahora puedo usarlo más tarde. Aquí un ejemplo, donde el valor simplemente se escribe como comentario HTML para fines de depuración:

<!-- ${URI} -->

Con su clase constante, puede importar su clase y asignar las constantes a las variables locales. Sé que mi respuesta es una especie de truco rápido, pero la pregunta también surge cuando uno quiere definir constantes directamente en el JSP.

koppor
fuente
1
lol ¿por qué no usar Scriptlets directamente si así es como inicializas las variables EL?
Navin
Tres líneas en la parte superior son un desastre y luego limpia EL en el resto del JSP ^^.
koppor
1
@koppoor supongo que sí. Solo voy a usar <%=URI%>: P
Navin
1
Tenía un lugar donde un directo <%=URI%>no funcionaba, pero esta técnica sí.
englebart
3

Sí tu puedes. Necesita una etiqueta personalizada (si no puede encontrarla en otro lugar). He hecho esto:

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

y la etiqueta se llama:

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

Todas las variables finales estáticas públicas se colocarán en un mapa indexado por su nombre de Java, por lo que si

public static final int MY_FIFTEEN = 15;

luego la etiqueta lo envolverá en un entero y puede hacer referencia a él en un JSP:

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

¡y no tienes que escribir getters!

Tim Sabin
fuente
3

Usted puede. Prueba de la siguiente manera

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

Probado en TomCat 7 y java6

Dmytro Boichenko
fuente
3
Eso se parece a SpEL y no a EL. ¿Estoy equivocado? Además, ¿funcionaría eso en un Tomcat5.5 más antiguo?
Pytry
2

Incluso sabiendo que es un poco tarde, e incluso sabiendo que esto es un pequeño truco, utilicé la siguiente solución para lograr el resultado deseado. Si eres un amante de las convenciones de nombres de Java, mi consejo es que dejes de leer aquí ...

Tener una clase como esta, definiendo constantes, agrupadas por clases vacías para crear una especie de jerarquía:

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

se puede usar desde java PERMISSION.PAGE.SEEpara recuperar el valor1L

Para lograr una posibilidad de acceso similar desde dentro de EL-Expressions, hice esto: (Si hay un dios codificador, con suerte podría perdonarme: 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, la expresión EL para acceder a la misma se Longconvierte en: #{PERMISSION.PAGE.SEE}- igualdad para Java y EL-Access. Sé que esto está fuera de cualquier convención, pero funciona perfectamente bien.

perder
fuente
2

@Bozho ya dio una gran respuesta

Por lo general, coloca este tipo de constantes en un objeto de configuración (que tiene captadores y definidores) en el contexto del servlet y accede a ellas con $ {applicationScope.config.url}

Sin embargo, creo que se necesita un ejemplo, por lo que aporta un poco más de claridad y ahorra tiempo a alguien.

@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
fuente
0

Hay una solución que no es exactamente lo que desea, pero le permite activar casi lo mismo tocando scriptlets de una manera bastante mínima. Puede usar scriptlet para poner valor en una variable JSTL y usar código JSTL limpio más adelante en la 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
fuente
1
No veo por qué esto fue rechazado. Este es el mismo patrón que la opción n. ° 3 en: stackoverflow.com/a/16692821/274677
Marcus Junius Brutus