Jak odwoływać się do stałych w EL?

106

W jaki sposób odwołujesz się do stałych za pomocą EL na stronie JSP?

Mam interfejs Addressesze stałą o nazwie URL. Wiem, że mogę odwołać się do tego rysikiem, przechodząc: <%=Addresses.URL%>ale jak to zrobić za pomocą EL?

neutrino tau
źródło

Odpowiedzi:

156

EL 3.0 lub nowszy

Jeśli korzystasz już z języka Java EE 7 / EL 3.0, program @page importzaimportuje również stałe klasy w zakresie EL.

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

Będzie to pod okładkami importowane przez ImportHandler#importClass()i dostępne jako ${YourConstants.FOO}.

Zauważ, że wszystkie java.lang.*klasy są już niejawnie zaimportowane i dostępne, tak jak ${Boolean.TRUE}i ${Integer.MAX_VALUE}. Wymaga to tylko nowszego serwera kontenerów Java EE 7, ponieważ wczesne wersje miały w tym błędy. Np. Zawodzą GlassFish 4.0 i Tomcat 8.0.0-1x, ale GlassFish 4.1+ i Tomcat 8.0.2x + działają. I musisz mieć absolutną pewność, że web.xmldeklarowana jest zgodność z najnowszą wersją serwletu obsługiwaną przez serwer. Zatem w przypadku web.xmldeklaracji zgodności z serwletem 2.5 lub starszym żadna z funkcji Servlet 3.0+ nie będzie działać.

Należy również pamiętać, że ta funkcja jest dostępna tylko w JSP, a nie w Facelets. W przypadku JSF + Facelets najlepszym rozwiązaniem jest użycie OmniFaces,<o:importConstants> jak poniżej:

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

Lub dodanie odbiornika kontekstu EL, który wywołuje ImportHandler#importClass()jak poniżej:

@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 lub starszy

Nie jest to możliwe w EL 2.2 i starszych. Istnieje kilka alternatyw:

  1. Umieść je w tym, Map<String, Object>który umieścisz w zakresie aplikacji. W EL wartości map są dostępne w zwykły sposób w Javabean przez ${map.key}lub ${map['key.with.dots']}.

  2. Korzystanie <un:useConstants>z niestandardowego taglib ( tutaj repozytorium maven2 ):

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

    W ten sposób są one dostępne w zwykły sposób w Javabean przez ${constants.FOO}.

  3. Użyj CCC Javarancha, <ccc:constantsMap>jak opisano gdzieś na dole tego artykułu .

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

    W ten sposób są one również dostępne w zwykły sposób w Javabean ${constants.FOO}.

  4. Jeśli używasz JSF2, następnie można użyć <o:importConstants>z OmniFaces .

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

    W ten sposób są one również dostępne w zwykły sposób w Javabean #{YourConstants.FOO}.

  5. Utwórz klasę opakowującą, która zwraca je za pomocą metod pobierających w stylu Javabean.

  6. Utwórz niestandardowy program rozpoznawania nazw EL, który najpierw skanuje obecność stałej, a jeśli jej nie ma, następnie deleguje do domyślnego programu rozpoznawania nazw, w przeciwnym razie zwraca stałą wartość.

BalusC
źródło
4
Znalazłem to pytanie, ponieważ miałem ten sam problem, gdy próbowałem użyć statycznego pola listy z tagiem form: opcje. Udało mi się to uruchomić, dodając niestatyczny getter, który zwraca statyczną listę. To trochę niezdarne, ale hej, to jest rozwój JSP dla ciebie!
spaaarky21
1
Czy masz jakiś przykład, jak skonfigurować to dla JSF, jeśli fasolki są zarządzane wiosną? Dzięki z góry.
Lodger
2
@Lodger: Nie robię wiosny.
BalusC
2
Czy unstandard-taglibprojekt Dżakarta wciąż żyje? czy jest jakaś alternatywa?
davioooh
1
Czy istnieje sposób na wykorzystanie tych technik do wyliczeń?
Niklas Peter
11

Poniższe informacje nie odnoszą się ogólnie do EL, ale tylko do SpEL (Spring EL) (testowane z 3.2.2.RELEASE na Tomcat 7). Myślę, że warto o tym tutaj wspomnieć na wypadek, gdyby ktoś szukał JSP i EL (ale używa JSP ze Spring).

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

Zazwyczaj tego rodzaju stałe umieszcza się w Configurationobiekcie (który ma metody pobierające i ustawiające) w kontekście serwletu i uzyskuje do nich dostęp za pomocą${applicationScope.config.url}

Bozho
źródło
Trochę nowicjuszem, jeśli chodzi o jsp - czy mógłbyś to dokładniej wyjaśnić?
neutrino tau
1
@ tau-neutrino: Właściwie to jest proste. Utwórz klasę z urlwłaściwością String, nazwij ją Configuration, utwórz jej instancję i ustaw urldowolną wartość. Następnie ustaw ten Configurationobiekt ServletContext. Zrób coś takiego jak servletContext.setAttribute("config", config). I gotowe.
Adeel Ansari
Jaka jest różnica między proponowanym rozwiązaniem a zwykłym dodaniem stałej jako atrybutu zmiennej ServletContext? Czy po prostu możesz dokładniej klasyfikować stałe? np applicationScope.config.urlvs applicationScope.url.
oneuv 08
7

Nie możesz. Jest zgodny z konwencją Java Bean. Więc musisz mieć do tego getter.

Adeel Ansari
źródło
5

Właściwości statyczne nie są dostępne w EL. Sposób obejścia tego problemu polega na utworzeniu zmiennej niestatycznej, która przypisuje się do wartości statycznej.

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

Używam lomboka do generowania gettera i settera, więc to całkiem nieźle. Twój EL wygląda tak:

${bean.manager_role}

Pełny kod na http://www.ninthavenue.com.au/java-static-constants-in-jsp-and-jsf-el

Roger Keays
źródło
5

Zrealizowałem tak:

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

W następnym kroku umieść instancję tej klasy w servlerContext

public class ApplicationInitializer implements ServletContextListener {


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

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

dodaj odbiornik do web.xml

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

dostęp w jsp

${Constants.PAGE_SIZE}
Serhii Bohutskyi
źródło
4

Na początku definiuję stałą w moim jsp:

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

Dołączam rdzeń taglib do mojej strony JSP:

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

Następnie udostępniam stałą EL, wykonując następujące oświadczenie:

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

Teraz mogę tego użyć później. Oto przykład, w którym wartość jest zapisywana jako komentarz HTML do celów debugowania:

<!-- ${URI} -->

Za pomocą swojej stałej klasy możesz po prostu zaimportować swoją klasę i przypisać stałe do zmiennych lokalnych. Wiem, że moja odpowiedź to rodzaj szybkiego hackowania, ale pytanie pojawia się również, gdy chce się zdefiniować stałe bezpośrednio na JSP.

koppor
źródło
1
lol, dlaczego nie użyć Scriptlets bezpośrednio, jeśli w ten sposób inicjalizujesz zmienne EL?
Navin
Trzy linie na górze to bałagan, a następnie wyczyść EL przez resztę JSP ^^.
koppor
1
@koppoor chyba tak. Użyję tylko <%=URI%>: P
Navin
1
Miałem miejsce, w którym direct <%=URI%>nie działał, ale ta technika działała.
englebart
3

Tak, możesz. Potrzebujesz niestandardowego tagu (jeśli nie możesz go znaleźć gdzie indziej). Zrobiłem to:

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

a tag nazywa się:

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

Wszystkie publiczne statyczne zmienne końcowe zostaną umieszczone w Mapie indeksowanej według ich nazwy w Javie, więc jeśli

public static final int MY_FIFTEEN = 15;

wtedy tag zawinie to w liczbę całkowitą i możesz odwołać się do niego na stronie JSP:

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

i nie musisz pisać getters!

Tim Sabin
źródło
3

Możesz. Spróbuj naśladować

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

Przetestowano na TomCat 7 i java6

Dmytro Boichenko
źródło
3
To wygląda jak SpEL, a nie EL. Czy się mylę? Czy to zadziała w starszym Tomcat5.5?
Pytry
2

Nawet wiedząc, że jest trochę za późno, a nawet wiedząc, że to mały hack - zastosowałem następujące rozwiązanie, aby osiągnąć pożądany rezultat. Jeśli jesteś miłośnikiem konwencji nazewnictwa języka Java, radzę przestać czytać tutaj ...

Posiadanie takiej klasy, definiowanie stałych, pogrupowanych według pustych klas, aby stworzyć rodzaj hierarchii:

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

może być używany z poziomu java, PERMISSION.PAGE.SEEaby pobrać wartość1L

Aby uzyskać podobną możliwość dostępu z poziomu EL-Expressions, zrobiłem to: (Jeśli istnieje bóg kodujący - mam nadzieję, że może mi wybaczyć: 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();
    }
}

na koniec EL-Expression, aby uzyskać dostęp do tego samego, Longstaje się: #{PERMISSION.PAGE.SEE}- równością dla Java i EL-Access. Wiem, że to jest poza jakąkolwiek konwencją, ale działa doskonale.

dognose
źródło
2

@Bozho już udzielił świetnej odpowiedzi

Zazwyczaj tego rodzaju stałe umieszcza się w obiekcie konfiguracyjnym (który ma metody pobierające i ustawiające) w kontekście serwletu i uzyskuje do nich dostęp za pomocą $ {applicationScope.config.url}

Uważam jednak, że potrzebny jest przykład, który daje trochę więcej klarowności i oszczędza komuś czas

@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
źródło
0

Istnieje obejście, które nie jest dokładnie tym, czego chcesz, ale pozwala na prawie takie samo działanie z dotykaniem skryptów w dość minimalny sposób. Możesz użyć skryptletu, aby umieścić wartość w zmiennej JSTL i użyć czystego kodu JSTL w dalszej części strony.

<%@ 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
źródło
1
Nie rozumiem, dlaczego głosowano w dół. To jest ten sam wzorzec, co opcja nr 3 w: stackoverflow.com/a/16692821/274677
Marcus Junius Brutus