Làm thế nào để tham chiếu các hằng số trong EL?


106

Làm thế nào để bạn tham chiếu một hằng số với EL trên trang JSP?

Tôi có một giao diện Addressesvới một hằng số được đặt tên URL. Tôi biết tôi có thể tham chiếu nó với một script bằng cách truy cập:, <%=Addresses.URL%>nhưng làm cách nào để thực hiện việc này bằng EL?

Câu trả lời:


156

EL 3.0 hoặc mới hơn

Nếu bạn đã sử dụng Java EE 7 / EL 3.0, thì hàm @page importcũng sẽ nhập các hằng số lớp trong phạm vi EL.

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

Điều này sẽ được nhập khẩu qua ImportHandler#importClass()và có sẵn dưới dạng ${YourConstants.FOO}.

Lưu ý rằng tất cả java.lang.*các lớp đã được nhập ngầm và có sẵn như vậy ${Boolean.TRUE}${Integer.MAX_VALUE}. Điều này chỉ yêu cầu một máy chủ chứa Java EE 7 mới hơn vì các phiên bản đầu tiên có lỗi trong việc này. Ví dụ: GlassFish 4.0 và Tomcat 8.0.0-1x không thành công, nhưng GlassFish 4.1+ và Tomcat 8.0.2x + hoạt động. Và bạn cần hoàn toàn đảm bảo rằng bạn web.xmlđược khai báo phù hợp với phiên bản servlet mới nhất được máy chủ hỗ trợ. Do đó, với một web.xmlServlet 2.5 hoặc cũ hơn được khai báo là phù hợp, không tính năng nào trong số Servlet 3.0+ sẽ hoạt động.

Cũng xin lưu ý rằng tiện ích này chỉ có sẵn trong JSP và không có trong Khuôn mặt. Trong trường hợp JSF + Khuôn mặt,<o:importConstants> cách tốt nhất của bạn là sử dụng OmniFaces như sau:

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

Hoặc thêm một trình nghe ngữ cảnh EL gọi ImportHandler#importClass()như sau:

@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 trở lên

Điều này không thể thực hiện được ở EL 2.2 trở lên. Có một số lựa chọn thay thế:

  1. Đặt chúng trong một Map<String, Object>mà bạn đặt trong phạm vi ứng dụng. Trong EL, các giá trị bản đồ có thể truy cập theo cách thông thường của người Javabean bằng ${map.key}hoặc ${map['key.with.dots']}.

  2. Sử dụng <un:useConstants>các unstandard taglib (maven2 repo đây ):

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

    Bằng cách này, họ có thể truy cập theo cách Javabean thông thường ${constants.FOO}.

  3. Sử dụng CCC của Javaranch <ccc:constantsMap>như được mô tả ở đâu đó ở cuối bài viết này .

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

    Bằng cách này, họ cũng có thể truy cập theo cách Javabean thông thường ${constants.FOO}.

  4. Nếu bạn đang sử dụng JSF2, sau đó bạn có thể sử dụng <o:importConstants>các OmniFaces .

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

    Bằng cách này, họ cũng có thể truy cập theo cách Javabean thông thường #{YourConstants.FOO}.

  5. Tạo một lớp wrapper trả về chúng thông qua các phương thức getter kiểu Javabean.

  6. Tạo một trình phân giải EL tùy chỉnh, trước tiên sẽ quét sự hiện diện của một hằng số và nếu không có, sau đó ủy quyền cho trình phân giải mặc định, nếu không sẽ trả về giá trị hằng số thay thế.


4
Tôi tìm thấy câu hỏi này vì tôi đang gặp sự cố tương tự khi cố gắng sử dụng trường Danh sách tĩnh với thẻ biểu mẫu: tùy chọn. Tôi đã có thể làm cho nó hoạt động bằng cách thêm một getter không tĩnh trả về danh sách tĩnh. Đó là một chút kludgy nhưng này, đó là phát triển JSP cho bạn!
spaaarky21

1
Bạn có ví dụ nào về cách cấu hình điều này cho JSF nếu các bean được quản lý bởi spring không? Thx trước.
Lodger

2
@Lodger: Tôi không làm Spring.
BalusC

2
unstandard-taglibDự án jakarta vẫn còn sống? có một số thay thế?
davioooh

1
Có cách nào để tận dụng kỹ thuật này cho enums không?
Niklas Peter

11

Những điều sau đây không áp dụng cho EL nói chung mà chỉ áp dụng cho SpEL (Spring EL) (được thử nghiệm với 3.2.2.RELEASE trên Tomcat 7). Tôi nghĩ điều đáng nói ở đây trong trường hợp ai đó tìm kiếm JSP và EL (nhưng sử dụng JSP với Spring).

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

9

Bạn thường đặt các loại hằng số này trong một Configurationđối tượng (có getters và setters) trong ngữ cảnh servlet và truy cập chúng bằng${applicationScope.config.url}


Một chút của một người mới ở đây khi nói đến jsp- bạn có thể giải thích điều đó đầy đủ hơn không?
tau-neutrino

1
@ tau-neutrino: Thực ra thì đơn giản thôi. Tạo một lớp với thuộc urltính String, đặt tên cho nó Configuration, khởi tạo nó và đặt thành urlbất cứ thứ gì bạn thích. Sau đó đặt Configurationđối tượng đó vào ServletContext. Làm điều gì đó như servletContext.setAttribute("config", config),. Và ở đó bạn đi.
Adeel Ansari

Sự khác biệt giữa giải pháp được đề xuất của bạn và chỉ cần thêm hằng số làm thuộc tính của ServletContext? Chỉ là bạn có thể phân loại các hằng số gọn gàng hơn? ví dụ: applicationScope.config.urlvs applicationScope.url.
theyuv

7

Bạn không thể. Nó tuân theo quy ước Java Bean. Vì vậy, bạn phải có một getter cho nó.


5

Thuộc tính tĩnh không thể truy cập được trong EL. Cách giải quyết mà tôi sử dụng là tạo một biến không tĩnh tự gán giá trị tĩnh.

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

Tôi sử dụng lombok để tạo getter và setter nên điều đó khá tốt. EL của bạn trông như thế này:

${bean.manager_role}

Mã đầy đủ tại http://www.ninthavenue.com.au/java-static-constants-in-jsp-and-jsf-el


5

Tôi đã triển khai như sau:

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

Bước tiếp theo đặt phiên bản của lớp này vào servlerContext

public class ApplicationInitializer implements ServletContextListener {


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

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

thêm trình nghe vào web.xml

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

truy cập trong jsp

${Constants.PAGE_SIZE}

4

Tôi đang xác định một hằng số trong jsp của mình ngay từ đầu:

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

Tôi bao gồm taglib cốt lõi trong JSP của mình:

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

Sau đó, tôi cung cấp hằng số cho EL bằng câu lệnh sau:

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

Bây giờ, tôi có thể sử dụng nó sau. Đây là một ví dụ, trong đó giá trị chỉ được viết dưới dạng nhận xét HTML cho mục đích gỡ lỗi:

<!-- ${URI} -->

Với lớp hằng của bạn, bạn chỉ có thể nhập lớp của mình và gán các hằng số cho các biến cục bộ. Tôi biết rằng câu trả lời của tôi là một loại hack nhanh, nhưng câu hỏi cũng nảy sinh khi người ta muốn xác định hằng số trực tiếp trong JSP.


1
lol tại sao không sử dụng Scriptlets trực tiếp nếu đó là cách bạn khởi tạo các biến EL?
Navin

Ba dòng trên đầu là một mớ hỗn độn và sau đó làm sạch EL trong suốt phần còn lại của JSP ^^.
koppor

1
@koppoor Tôi đoán vậy. Tôi chỉ sẽ sử dụng <%=URI%>: P
Navin

1
Tôi đã có một nơi mà một trực tiếp <%=URI%>không hoạt động, nhưng kỹ thuật này đã làm được.
englebart

3

Có, bạn có thể. Bạn cần một thẻ tùy chỉnh (nếu bạn không thể tìm thấy nó ở một nơi khác). Tôi đã làm điều này:

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

và thẻ được gọi là:

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

Tất cả các biến cuối cùng tĩnh công khai sẽ được đưa vào một Bản đồ được lập chỉ mục bằng tên Java của chúng, vì vậy nếu

public static final int MY_FIFTEEN = 15;

thì thẻ sẽ bọc nó trong một Số nguyên và bạn có thể tham chiếu nó trong JSP:

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

và bạn không cần phải viết getters!


3

Bạn có thể. Hãy thử theo cách sau

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

Đã thử nghiệm trên TomCat 7 và java6


3
Đó trông giống như SpEL, và không phải EL. Tôi có nhầm không? Ngoài ra, điều đó có hoạt động trong Tomcat5.5 cũ hơn không?
Pytry

2

Ngay cả khi biết nó hơi muộn, và ngay cả khi biết đây là một chút hack - tôi đã sử dụng giải pháp sau để đạt được kết quả mong muốn. Nếu bạn là một người yêu thích Java-Đặt tên-Quy ước, lời khuyên của tôi là dừng đọc ở đây ...

Có một lớp như thế này, xác định Hằng số, được nhóm bởi các lớp trống để tạo ra loại cấu trúc phân cấp:

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

có thể được sử dụng từ bên trong java PERMISSION.PAGE.SEEđể lấy giá trị1L

Để đạt được khả năng truy cập simliar từ bên trong EL-Expressions, tôi đã làm điều này: (Nếu có một vị thần mã hóa - hy vọng anh ấy có thể tha thứ cho tôi: 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();
    }
}

cuối cùng, EL-Expression để truy cập giống nhau Longtrở thành: #{PERMISSION.PAGE.SEE}- bình đẳng cho Java và EL-Access. Tôi biết điều này không theo quy ước nào, nhưng nó hoạt động hoàn toàn tốt.


2

@Bozho đã cung cấp một câu trả lời tuyệt vời

Bạn thường đặt các loại hằng số này trong một đối tượng Cấu hình (có bộ nhận và bộ định tuyến) trong ngữ cảnh servlet và truy cập chúng bằng $ {applicationScope.config.url}

Tuy nhiên, tôi cảm thấy cần một ví dụ để nó mang lại sự rõ ràng hơn một chút và tiết kiệm thời gian cho ai đó

@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

Có một cách giải quyết không chính xác như những gì bạn muốn, nhưng cho phép bạn hoạt động gần như tương tự với việc chạm vào các script theo cách khá tối thiểu. Bạn có thể sử dụng scriptlet để đặt giá trị vào biến JSTL và sử dụng mã JSTL sạch sau này trong trang.

<%@ 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
Tôi không hiểu tại sao điều này lại bị bỏ phiếu thấp. Đây là mô hình tương tự như tùy chọn # 3 trong: stackoverflow.com/a/16692821/274677
Marcus Junius Brutus
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.